Files
nwipe/src/gui.c
PartialVolume 4b1c7007ff Various fixes to methods
1. Allows zero fill to be verified when blanking off.
Prior to this if you wanted to verify a zero pass,
blanking had to be on. This meant a zero pass, then a
blanking pass then a verify, effectively two zero passes
and a verify. This is now fixed so you can now do a zero
pass with verification without a blanking pass. This knocks
a third off the wipe time of a zero fill with verification.

This also means all other methods can have either all their
passes or just the last pass verified without blanking being
enabled.

2. OPS2 method requires the last pass to be random, the GUI
now disables the use of the blanking option for this message
and displays a warning message that a final blanking pass is
not allowed for OPS2. It never did a final blanking pass
anyway, even if it was selected, but this makes it clearer in
the GUI.

3. The caculate_round_size() function was improved by reducing
some duplicated code and bring the full calculation into this function.

4. On completion of each pass or verification the total number of
bytes written or read for each pass or verification is logged.
2021-06-04 10:20:23 +01:00

2954 lines
94 KiB
C

/*
* gui.c: An ncurses GUI for nwipe.
*
* Copyright Darik Horn <dajhorn-dban@vanadac.com>.
*
* Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/* RATIONALE:
*
* This entire GUI is a non-portable task-specific thunk.
*
* The alternatives are, however, no better. The CDK is large and clumsy,
* and things like ncurses libmenu are not worth the storage overhead.
*
*/
#include <ncurses.h>
#include <panel.h>
#include <stdint.h>
#include <time.h>
#include "nwipe.h"
#include "context.h"
#include "method.h"
#include "prng.h"
#include "options.h"
#include "gui.h"
#include "pass.h"
#include "logging.h"
#include "version.h"
#define NWIPE_GUI_PANE 8
/* Header window: width, height, x coordinate, y coordinate. */
#define NWIPE_GUI_HEADER_W COLS
#define NWIPE_GUI_HEADER_H 1
#define NWIPE_GUI_HEADER_X 0
#define NWIPE_GUI_HEADER_Y 0
/* Footer window: width, height, x coordinate, y coordinate. */
#define NWIPE_GUI_FOOTER_W COLS
#define NWIPE_GUI_FOOTER_H 1
#define NWIPE_GUI_FOOTER_X 0
#define NWIPE_GUI_FOOTER_Y ( LINES - 1 )
/* Options window: width, height, x coorindate, y coordinate. */
#define NWIPE_GUI_OPTIONS_W 44
#define NWIPE_GUI_OPTIONS_H 7
#define NWIPE_GUI_OPTIONS_Y 1
#define NWIPE_GUI_OPTIONS_X 0
/* Options fields, relative to their window. */
#define NWIPE_GUI_OPTIONS_TAB 10
#define NWIPE_GUI_OPTIONS_ENTROPY_Y 1
#define NWIPE_GUI_OPTIONS_ENTROPY_X 1
#define NWIPE_GUI_OPTIONS_PRNG_Y 2
#define NWIPE_GUI_OPTIONS_PRNG_X 1
#define NWIPE_GUI_OPTIONS_METHOD_Y 3
#define NWIPE_GUI_OPTIONS_METHOD_X 1
#define NWIPE_GUI_OPTIONS_VERIFY_Y 4
#define NWIPE_GUI_OPTIONS_VERIFY_X 1
#define NWIPE_GUI_OPTIONS_ROUNDS_Y 5
#define NWIPE_GUI_OPTIONS_ROUNDS_X 1
/* Stats window: width, height, x coordinate, y coordinate. */
#define NWIPE_GUI_STATS_W ( COLS - 44 )
#define NWIPE_GUI_STATS_H 7
#define NWIPE_GUI_STATS_Y 1
#define NWIPE_GUI_STATS_X 44
/* Stats fields, relative to their window. */
#define NWIPE_GUI_STATS_RUNTIME_Y 1
#define NWIPE_GUI_STATS_RUNTIME_X 1
#define NWIPE_GUI_STATS_ETA_Y 2
#define NWIPE_GUI_STATS_ETA_X 1
#define NWIPE_GUI_STATS_LOAD_Y 3
#define NWIPE_GUI_STATS_LOAD_X 1
#define NWIPE_GUI_STATS_THROUGHPUT_Y 4
#define NWIPE_GUI_STATS_THROUGHPUT_X 1
#define NWIPE_GUI_STATS_ERRORS_Y 5
#define NWIPE_GUI_STATS_ERRORS_X 1
#define NWIPE_GUI_STATS_TAB 16
/* Select window: width, height, x coordinate, y coordinate. */
#define NWIPE_GUI_MAIN_W COLS
#define NWIPE_GUI_MAIN_H ( LINES - NWIPE_GUI_MAIN_Y - 1 )
#define NWIPE_GUI_MAIN_Y 8
#define NWIPE_GUI_MAIN_X 0
#define SKIP_DEV_PREFIX 5
/* Window pointers. */
WINDOW* footer_window;
WINDOW* header_window;
WINDOW* main_window;
WINDOW* options_window;
WINDOW* stats_window;
PANEL* footer_panel;
PANEL* header_panel;
PANEL* main_panel;
PANEL* options_panel;
PANEL* stats_panel;
/* Options window title. */
const char* options_title = " Options ";
/* Statistics window title. */
const char* stats_title = " Statistics ";
/* Footer labels. */
const char* main_window_footer = "S=Start m=Method p=PRNG v=Verify r=Rounds b=Blanking Space=Select CTRL+C=Quit";
const char* main_window_footer_warning_lower_case_s = " WARNING: To start the wipe press SHIFT+S (uppercase S) ";
const char* main_window_fotter_warning_no_blanking_with_ops2 =
" WARNING: Zero blanking is not allowed with ops2 method ";
const char* main_window_fotter_warning_no_blanking_with_verify_only =
" WARNING: Zero blanking is not allowed with verify method ";
const char* main_window_footer_warning_no_drive_selected =
" No drives selected, use spacebar to select a drive, then press S to start ";
/* Oddly enough, placing extra quotes around the footer strings fixes corruption to the right
* of the footer message when the terminal is resized, a quirk in ncurses? - DO NOT REMOVE THE \" */
const char* selection_footer = "\"J=Down K=Up Space=Select Backspace=Cancel Ctrl+C=Quit\"";
const char* end_wipe_footer = "\"B=Blank screen Ctrl+C=Quit\"";
const char* rounds_footer = "\"Left=Erase Esc=Cancel Ctrl+C=Quit\"";
const char* wipes_finished_footer = "Wipe finished - press enter to exit. Logged to STDOUT";
/* The number of lines available in the terminal */
int stdscr_lines;
/* The number of columns available in the terminal */
int stdscr_cols;
/* The size of the terminal lines when previously checked */
int stdscr_lines_previous;
/* The size of the terminal columns when previously checked */
int stdscr_cols_previous;
void nwipe_gui_title( WINDOW* w, const char* s )
{
/**
* Prints the string 's' centered on the first line of the window 'w'.
*/
/* The number of lines in the window. (Not used.) */
int wy;
(void) wy; /* flag wy not used to the compiler, to silence warning */
/* The number of columns in the window. */
int wx;
/* Get the window dimensions. */
getmaxyx( w, wy, wx );
/*Calculate available total margin */
int margin = ( wx - strlen( s ) );
if( margin < 0 )
{
margin = 0;
}
/* Print the title. */
mvwprintw( w, 0, margin / 2, "%s", s );
} /* nwipe_gui_title */
void nwipe_gui_init( void )
{
/**
* Initializes the ncurses gui.
*/
/* Initialize the screen. */
initscr();
/* Disable TTY line buffering. */
cbreak();
/* Disable TTY echo. */
noecho();
/* Enable most special keys. */
keypad( stdscr, TRUE );
if( has_colors() )
{
/* Initialize color capabilities. */
start_color();
if( can_change_color() )
{
/* Redefine cyan to gray. */
init_color( COLOR_CYAN, 128, 128, 128 );
}
/* Set white on blue as the emphasis color. */
init_pair( 1, COLOR_WHITE, COLOR_BLUE );
/* Set gray (or cyan) on blue as the normal color. */
init_pair( 2, COLOR_CYAN, COLOR_BLUE );
/* Set red on blue as the hilite color. */
init_pair( 3, COLOR_RED, COLOR_BLUE );
/* Set blue on white as the color for the header and footer windows. */
init_pair( 4, COLOR_BLUE, COLOR_WHITE );
/* Set white on green for success messages. */
init_pair( 5, COLOR_WHITE, COLOR_GREEN );
/* Set white on red for failure messages. */
init_pair( 6, COLOR_WHITE, COLOR_RED );
/* Set black on black for when hiding the display. */
init_pair( 7, COLOR_BLACK, COLOR_BLACK );
/* Set green on blue for reverse bold messages */
init_pair( 8, COLOR_GREEN, COLOR_WHITE );
/* Set green on blue for reverse bold error messages */
init_pair( 9, COLOR_RED, COLOR_WHITE );
/* Set black on yellow for warning messages */
init_pair( 10, COLOR_BLACK, COLOR_YELLOW );
/* Set the background style. */
wbkgdset( stdscr, COLOR_PAIR( 1 ) | ' ' );
}
/* Clear the screen. */
wclear( stdscr );
/* Create the header window. */
nwipe_gui_create_header_window();
/* Create the footer window and panel */
nwipe_gui_create_footer_window( main_window_footer );
/* Create the options window and panel */
nwipe_gui_create_options_window();
/* Create the stats window. */
nwipe_gui_create_stats_window();
/* Create a new main window and panel */
nwipe_gui_create_main_window();
update_panels();
doupdate();
/* Hide the cursor. */
curs_set( 0 );
} /* nwipe_gui_init */
void nwipe_gui_free( void )
{
/**
* Releases the ncurses gui.
*
*/
/* Free ncurses resources. */
if( del_panel( footer_panel ) != OK )
{
nwipe_log( NWIPE_LOG_ERROR, "Deleting footer panel failed!." );
}
if( del_panel( header_panel ) != OK )
{
nwipe_log( NWIPE_LOG_ERROR, "Deleting header panel failed!." );
}
if( del_panel( main_panel ) != OK )
{
nwipe_log( NWIPE_LOG_ERROR, "Deleting main panel failed!." );
}
if( del_panel( options_panel ) != OK )
{
nwipe_log( NWIPE_LOG_ERROR, "Deleting options panel failed!." );
}
if( del_panel( stats_panel ) != OK )
{
nwipe_log( NWIPE_LOG_ERROR, "Deleting stats panel failed!." );
}
if( delwin( footer_window ) != OK )
{
nwipe_log( NWIPE_LOG_ERROR, "Deleting footer window failed!." );
}
if( delwin( header_window ) != OK )
{
nwipe_log( NWIPE_LOG_ERROR, "Deleting header window failed!." );
}
if( delwin( main_window ) != OK )
{
nwipe_log( NWIPE_LOG_ERROR, "Deleting main window failed!." );
}
if( delwin( options_window ) != OK )
{
nwipe_log( NWIPE_LOG_ERROR, "Deleting options window failed!." );
}
if( delwin( stats_window ) != OK )
{
nwipe_log( NWIPE_LOG_ERROR, "Deleting stats window failed!." );
}
if( endwin() != OK )
{
nwipe_log( NWIPE_LOG_ERROR, "Curses endwin() failed !" );
}
} /* nwipe_gui_free */
void nwipe_gui_create_main_window()
{
/* Create the main window. */
main_window = newwin( NWIPE_GUI_MAIN_H, NWIPE_GUI_MAIN_W, NWIPE_GUI_MAIN_Y, NWIPE_GUI_MAIN_X );
main_panel = new_panel( main_window );
if( has_colors() )
{
/* Set the background style. */
wbkgdset( main_window, COLOR_PAIR( 1 ) | ' ' );
/* Apply the color change. */
wattron( main_window, COLOR_PAIR( 1 ) );
}
/* Clear the main window. */
werase( main_window );
/* Add a border. */
box( main_window, 0, 0 );
/* refresh main window */
wnoutrefresh( main_window );
} /* nwipe_gui_create_main_window */
void nwipe_gui_create_header_window()
{
/* Create the header window. */
header_window = newwin( NWIPE_GUI_HEADER_H, NWIPE_GUI_HEADER_W, NWIPE_GUI_HEADER_Y, NWIPE_GUI_HEADER_X );
header_panel = new_panel( header_window );
if( has_colors() )
{
/* Set the background style of the header window. */
wbkgdset( header_window, COLOR_PAIR( 4 ) | ' ' );
}
/* Clear the header window. */
werase( header_window );
/* Print the product banner. */
nwipe_gui_title( header_window, banner );
/* Refresh the header window */
wnoutrefresh( header_window );
} /* nwipe_gui_create_header_window */
void nwipe_gui_create_footer_window( const char* footer_text )
{
/* Create the footer window. */
footer_window = newwin( NWIPE_GUI_FOOTER_H, NWIPE_GUI_FOOTER_W, NWIPE_GUI_FOOTER_Y, NWIPE_GUI_FOOTER_X );
footer_panel = new_panel( footer_window );
if( has_colors() )
{
/* Set the background style of the footer window. */
wbkgdset( footer_window, COLOR_PAIR( 4 ) | ' ' );
}
/* Erase the footer window. */
werase( footer_window );
/* Add help text to the footer */
nwipe_gui_title( footer_window, footer_text );
/* Refresh the footer window */
wnoutrefresh( footer_window );
} /* nwipe_gui_create_footer_window */
void nwipe_gui_amend_footer_window( const char* footer_text )
{
/* Clear the footer window. */
werase( footer_window );
/* Add help text to the footer */
nwipe_gui_title( footer_window, footer_text );
/* Refresh the footer window */
wnoutrefresh( footer_window );
} /* nwipe_gui_amend_footer_window */
void nwipe_gui_create_options_window()
{
/* Create the options window. */
options_window = newwin( NWIPE_GUI_OPTIONS_H, NWIPE_GUI_OPTIONS_W, NWIPE_GUI_OPTIONS_Y, NWIPE_GUI_OPTIONS_X );
options_panel = new_panel( options_window );
if( has_colors() )
{
/* Set the background style of the options window. */
wbkgdset( options_window, COLOR_PAIR( 1 ) | ' ' );
/* Apply the color change to the options window. */
wattron( options_window, COLOR_PAIR( 1 ) );
}
/* Clear the options window. */
werase( options_window );
/* Add a border. */
box( options_window, 0, 0 );
} /* nwipe_gui_create_options_window */
void nwipe_gui_create_stats_window()
{
/* Create the stats window. */
stats_window = newwin( NWIPE_GUI_STATS_H, NWIPE_GUI_STATS_W, NWIPE_GUI_STATS_Y, NWIPE_GUI_STATS_X );
stats_panel = new_panel( stats_window );
if( has_colors() )
{
/* Set the background style of the stats window. */
wbkgdset( stats_window, COLOR_PAIR( 1 ) | ' ' );
/* Apply the color change to the stats window. */
wattron( stats_window, COLOR_PAIR( 1 ) );
}
/* Clear the new window. */
werase( stats_window );
/* Add a border. */
box( stats_window, 0, 0 );
/* Add a title. */
nwipe_gui_title( stats_window, stats_title );
/* Print field labels. */
mvwprintw( stats_window, NWIPE_GUI_STATS_RUNTIME_Y, NWIPE_GUI_STATS_RUNTIME_X, "Runtime: " );
mvwprintw( stats_window, NWIPE_GUI_STATS_ETA_Y, NWIPE_GUI_STATS_ETA_X, "Remaining: " );
mvwprintw( stats_window, NWIPE_GUI_STATS_LOAD_Y, NWIPE_GUI_STATS_LOAD_X, "Load Averages: " );
mvwprintw( stats_window, NWIPE_GUI_STATS_THROUGHPUT_Y, NWIPE_GUI_STATS_THROUGHPUT_X, "Throughput: " );
mvwprintw( stats_window, NWIPE_GUI_STATS_ERRORS_Y, NWIPE_GUI_STATS_ERRORS_X, "Errors: " );
} /* nwipe_gui_create_stats_window */
void nwipe_gui_create_all_windows_on_terminal_resize( const char* footer_text )
{
/* Get the terminal size */
getmaxyx( stdscr, stdscr_lines, stdscr_cols );
/* If the user has resized the terminal then recreate the windows and panels */
if( stdscr_cols_previous != stdscr_cols || stdscr_lines_previous != stdscr_lines )
{
/* Save the revised terminal size so we check whether the user has resized next time */
stdscr_lines_previous = stdscr_lines;
stdscr_cols_previous = stdscr_cols;
/* Clear the screen. */
wclear( stdscr );
/* Create a new header window and panel due to terminal size having changed */
nwipe_gui_create_header_window();
/* Create a new main window and panel due to terminal size having changed */
nwipe_gui_create_main_window();
/* Create a new footer window and panel due to terminal size having changed */
nwipe_gui_create_footer_window( footer_text );
/* Create a new options window and panel due to terminal size having changed */
nwipe_gui_create_options_window();
/* Create a new stats window and panel due to terminal size having changed */
nwipe_gui_create_stats_window();
/* Update the options window. */
nwipe_gui_options();
update_panels();
doupdate();
}
}
void nwipe_gui_select( int count, nwipe_context_t** c )
{
/**
* The primary user interface. Allows the user to
* change options and specify the devices to be wiped.
*
* @parameter count The number of contexts in the array.
* @parameter c An array of device contexts.
*
* @modifies c[].select Sets the select flag according to user input.
* @modifies options Sets program options according to to user input.
*
*/
extern int terminate_signal;
/* Widget labels. */
const char* select_title = " Disks and Partitions ";
/* The number of lines available in the window. */
int wlines;
/* The number of columns available in the window. */
int wcols;
/* The number of selection elements that we can show in the window. */
int slots;
/* The index of the element that is visible in the first slot. */
int offset = 0;
/* The selection focus. */
int focus = 0;
/* A generic loop variable. */
int i = 0;
/* User input buffer. */
int keystroke;
/* The current working line. */
int yy;
/* Flag, Valid key hit = 1, anything else = 0 */
int validkeyhit;
/* Counts number of drives and partitions that have been selected */
int number_of_selected_contexts = 0;
/* Control A toggle status -1=indefined, 0=all drives delected, 1=all drives selected */
int select_all_toggle_status = -1;
/* Get the terminal size */
getmaxyx( stdscr, stdscr_lines, stdscr_cols );
/* Save the terminal size so we check whether the user has resized */
stdscr_lines_previous = stdscr_lines;
stdscr_cols_previous = stdscr_cols;
do
{
nwipe_gui_create_all_windows_on_terminal_resize( main_window_footer );
/* There is one slot per line. */
getmaxyx( main_window, wlines, wcols );
/* Less two lines for the box and two lines for padding. */
slots = wlines - 4;
if( slots < 0 )
{
slots = 0;
}
/* The code here adjusts the offset value, required when the terminal is resized vertically */
if( slots > count )
{
offset = 0;
}
else
{
if( focus >= count )
{
/* The focus is already at the last element. */
focus = count - 1;
}
if( focus < 0 )
{
/* The focus is already at the last element. */
focus = 0;
}
}
if( count >= slots && slots > 0 )
{
offset = focus + 1 - slots;
if( offset < 0 )
{
offset = 0;
}
}
/* Clear the main window, necessary when switching selections such as method etc */
werase( main_window );
/* Refresh main window */
wnoutrefresh( main_window );
/* If the user selected an option the footer text would have changed.
* Here we set it back to the main key help text */
nwipe_gui_create_footer_window( main_window_footer );
/* Refresh the stats window */
wnoutrefresh( stats_window );
/* Refresh the options window */
wnoutrefresh( options_window );
/* Update the options window. */
nwipe_gui_options();
/* Initialize the line offset. */
yy = 2;
for( i = 0; i < slots && i < count; i++ )
{
/* Move to the next line. */
mvwprintw( main_window, yy++, 1, " " );
if( i + offset == focus )
{
if( c[focus]->select == NWIPE_SELECT_TRUE || c[focus]->select == NWIPE_SELECT_FALSE )
{
/* Print the 'enabled' cursor. */
waddch( main_window, ACS_RARROW );
}
else
{
/* Print the 'disabled' cursor. */
waddch( main_window, ACS_DIAMOND );
}
}
else
{
/* Print whitespace. */
waddch( main_window, ' ' );
}
/* In the event for the offset value somehow becoming invalid, this if statement will prevent a segfault
* and the else part will log the out of bounds values for debugging */
if( i + offset >= 0 && i + offset < count )
{
switch( c[i + offset]->select )
{
case NWIPE_SELECT_TRUE:
wprintw( main_window, " [wipe] %i. %s", ( i + offset + 1 ), c[i + offset]->device_label );
break;
case NWIPE_SELECT_FALSE:
/* Print an element that is not selected. */
wprintw( main_window, " [ ] %i. %s", ( i + offset + 1 ), c[i + offset]->device_label );
break;
case NWIPE_SELECT_TRUE_PARENT:
/* This element will be wiped when its parent is wiped. */
wprintw( main_window, " [****] %i. %s", ( i + offset + 1 ), c[i + offset]->device_label );
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", ( i + offset + 1 ), c[i + offset]->device_label );
break;
case NWIPE_SELECT_DISABLED:
/* We don't know how to wipe this device. (Iomega Zip drives.) */
wprintw( main_window, " [????] %s", "Unrecognized Device" );
break;
default:
/* TODO: Handle the sanity error. */
break;
} /* switch select */
}
else
{
nwipe_log( NWIPE_LOG_DEBUG,
"GUI.c,nwipe_gui_select(), scroll, array index out of bounds, i=%u, count=%u, slots=%u, "
"focus=%u, offset=%u",
i,
count,
slots,
focus,
offset );
}
} /* for */
if( offset > 0 )
{
mvwprintw( main_window, 1, wcols - 8, " More " );
waddch( main_window, ACS_UARROW );
}
if( count - offset > slots )
{
mvwprintw( main_window, wlines - 2, wcols - 8, " More " );
waddch( main_window, ACS_DARROW );
}
/* Draw a border around the menu window. */
box( main_window, 0, 0 );
/* Print a title. */
nwipe_gui_title( main_window, select_title );
/* Refresh the window. */
wnoutrefresh( main_window );
/* Output to physical screen */
doupdate();
/* Wait 250ms for input from getch, if nothing getch will then continue,
* This is necessary so that the while loop can be exited by the
* terminate_signal e.g.. the user pressing control-c to exit.
* Do not change this value, a higher value means the keys become
* sluggish, any slower and more time is spent unnecessarily looping
* which wastes CPU cycles.
*/
do
{
validkeyhit = 0;
timeout( 250 ); // block getch() for 250ms.
keystroke = getch(); // Get user input.
timeout( -1 ); // Switch back to blocking mode.
/* We don't necessarily use all of these. For future reference these are some CTRL+key values
* ^A - 1, ^B - 2, ^D - 4, ^E - 5, ^F - 6, ^G - 7, ^H - 8, ^I - 9, ^K - 11, ^L - 12, ^N - 14,
* ^O - 15, ^P - 16, ^R - 18, ^T - 20, ^U - 21, ^V - 22, ^W - 23, ^X - 24, ^Y - 25
* Use nwipe_log( NWIPE_LOG_DEBUG, "Key Name: %s - %u", keyname(keystroke),keystroke) to
* figure out what code is returned by what ever key combination */
switch( keystroke )
{
case KEY_DOWN:
case 'j':
case 'J':
validkeyhit = 1;
/* Increment the focus. */
focus += 1;
if( focus >= count )
{
/* The focus is already at the last element. */
focus = count - 1;
break;
}
if( focus - offset >= slots )
{
/* The next element is offscreen. Scroll down. */
offset += 1;
break;
}
break;
case KEY_UP:
case 'k':
case 'K':
validkeyhit = 1;
/* Decrement the focus. */
focus -= 1;
if( focus < 0 )
{
/* The focus is already at the last element. */
focus = 0;
break;
}
if( focus < offset )
{
/* The next element is offscreen. Scroll up. */
offset -= 1;
break;
}
break;
case KEY_ENTER:
case 10:
case ' ':
validkeyhit = 1;
/* TODO: This block should be made into a function. */
if( c[focus]->select == NWIPE_SELECT_TRUE )
{
/* Reverse the selection of this element. */
c[focus]->select = NWIPE_SELECT_FALSE;
if( c[focus]->device_part == 0 )
{
/* Sub-deselect all partitions and slices within this disk. */
for( i = 0; i < count; i++ )
{
if( c[i]->device_type == c[focus]->device_type
&& c[i]->device_host == c[focus]->device_host
&& c[i]->device_bus == c[focus]->device_bus
&& c[i]->device_target == c[focus]->device_target
&& c[i]->device_lun == c[focus]->device_lun && c[i]->device_part > 0 )
{
c[i]->select = NWIPE_SELECT_FALSE;
}
} /* for all contexts */
} /* if sub-deselect */
else
{
/* The number of selected partitions or slices within this disk. */
int j = 0;
for( i = 0; i < count; i++ )
{
if( c[i]->device_type == c[focus]->device_type
&& c[i]->device_host == c[focus]->device_host
&& c[i]->device_bus == c[focus]->device_bus
&& c[i]->device_target == c[focus]->device_target
&& c[i]->device_lun == c[focus]->device_lun && c[i]->device_part > 0
&& c[i]->select == NWIPE_SELECT_TRUE )
{
/* Increment the counter. */
j += 1;
}
} /* for all contexts */
if( j == 0 )
{
/* Find the parent disk of this partition or slice. */
for( i = 0; i < count; i++ )
{
if( c[i]->device_type == c[focus]->device_type
&& c[i]->device_host == c[focus]->device_host
&& c[i]->device_bus == c[focus]->device_bus
&& c[i]->device_target == c[focus]->device_target
&& c[i]->device_lun == c[focus]->device_lun && c[i]->device_part == 0 )
{
/* Enable the disk element. */
c[i]->select = NWIPE_SELECT_FALSE;
}
} /* for all contexts */
} /* if */
} /* else super-enable */
break;
} /* if NWIPE_SELECT_TRUE */
if( c[focus]->select == NWIPE_SELECT_FALSE )
{
/* Reverse the selection. */
c[focus]->select = NWIPE_SELECT_TRUE;
if( c[focus]->device_part == 0 )
{
/* Sub-select all partitions and slices within this disk. */
for( i = 0; i < count; i++ )
{
if( c[i]->device_type == c[focus]->device_type
&& c[i]->device_host == c[focus]->device_host
&& c[i]->device_bus == c[focus]->device_bus
&& c[i]->device_target == c[focus]->device_target
&& c[i]->device_lun == c[focus]->device_lun && c[i]->device_part > 0 )
{
c[i]->select = NWIPE_SELECT_TRUE_PARENT;
}
} /* for */
} /* if sub-select */
else
{
/* ASSERT: ( c[focus]->device_part > 0 ) */
/* Super-deselect the disk that contains this device. */
for( i = 0; i < count; i++ )
{
if( c[i]->device_type == c[focus]->device_type
&& c[i]->device_host == c[focus]->device_host
&& c[i]->device_bus == c[focus]->device_bus
&& c[i]->device_target == c[focus]->device_target
&& c[i]->device_lun == c[focus]->device_lun && c[i]->device_part == 0 )
{
c[i]->select = NWIPE_SELECT_FALSE_CHILD;
}
}
} /* else super-deselect */
break;
} /* if NWIPE_SELECT_FALSE */
/* TODO: Explain to the user why they can't change this. */
break;
case 'm':
case 'M':
validkeyhit = 1;
/* Run the method dialog. */
nwipe_gui_method();
break;
case 'p':
case 'P':
validkeyhit = 1;
/* Run the PRNG dialog. */
nwipe_gui_prng();
break;
case 'r':
case 'R':
validkeyhit = 1;
/* Run the rounds dialog. */
nwipe_gui_rounds();
break;
case 'v':
case 'V':
validkeyhit = 1;
/* Run the option dialog. */
nwipe_gui_verify();
break;
case 'b':
case 'B':
validkeyhit = 1;
if( nwipe_options.method == &nwipe_ops2 )
{
/* Warn the user about that zero blanking with the ops2 method is not allowed */
wattron( footer_window, COLOR_PAIR( 10 ) );
nwipe_gui_amend_footer_window( main_window_fotter_warning_no_blanking_with_ops2 );
doupdate();
sleep( 3 );
wattroff( footer_window, COLOR_PAIR( 10 ) );
/* After the delay return footer text back to key help */
nwipe_gui_amend_footer_window( main_window_footer );
doupdate();
break;
}
if( nwipe_options.method == &nwipe_verify )
{
/* Warn the user about that zero blanking with the ops2 method is not allowed */
wattron( footer_window, COLOR_PAIR( 10 ) );
nwipe_gui_amend_footer_window( main_window_fotter_warning_no_blanking_with_verify_only );
doupdate();
sleep( 3 );
wattroff( footer_window, COLOR_PAIR( 10 ) );
/* After the delay return footer text back to key help */
nwipe_gui_amend_footer_window( main_window_footer );
doupdate();
break;
}
/* Run the noblank dialog. */
nwipe_gui_noblank();
break;
case 'S':
/* User wants to start the wipe */
validkeyhit = 1;
/* Have any drives have been selected ? */
number_of_selected_contexts = 0;
for( i = 0; i < count; i++ )
{
if( c[i]->select == NWIPE_SELECT_TRUE )
{
number_of_selected_contexts += 1;
}
}
/* if no drives have been selected, print a warning on the footer */
if( number_of_selected_contexts == 0 )
{
wattron( footer_window, COLOR_PAIR( 10 ) );
nwipe_gui_amend_footer_window( main_window_footer_warning_no_drive_selected );
doupdate();
sleep( 3 );
wattroff( footer_window, COLOR_PAIR( 10 ) );
/* After the delay return footer text back to key help */
nwipe_gui_amend_footer_window( main_window_footer );
doupdate();
/* Remove any repeated S key strokes, without this the gui would hang
* for a period of time, i.e sleep above x number of repeated 's' keystrokes
* which could run into minutes */
do
{
timeout( 250 ); // block getch() for 250ms.
keystroke = getch(); // Get user input.
timeout( -1 ); // Switch back to blocking mode.
} while( keystroke == 'S' );
/* Remove the S from keystroke, which allows us to stay within the selection menu loop */
keystroke = 0;
}
break;
case 's':
/* user has mistakenly hit the lower case 's' instead of capital 'S' */
validkeyhit = 1;
/* Warn the user about their mistake */
wattron( footer_window, COLOR_PAIR( 10 ) );
nwipe_gui_amend_footer_window( main_window_footer_warning_lower_case_s );
doupdate();
sleep( 3 );
wattroff( footer_window, COLOR_PAIR( 10 ) );
/* After the delay return footer text back to key help */
nwipe_gui_amend_footer_window( main_window_footer );
doupdate();
/* Remove any repeated s key strokes, without this the gui would hang
* for a period of time, i.e sleep above x number of repeated 's' keystrokes
* which could run into minutes */
do
{
timeout( 250 ); // block getch() for 250ms.
keystroke = getch(); // Get user input.
timeout( -1 ); // Switch back to blocking mode.
} while( keystroke == 's' );
break;
case 1:
/* Ctrl A - Toggle select/deselect all drives */
validkeyhit = 1;
if( select_all_toggle_status == -1 || select_all_toggle_status == 0 )
{
for( i = 0; i < count; i++ )
{
c[i]->select = NWIPE_SELECT_TRUE;
}
select_all_toggle_status = 1;
}
else
{
if( select_all_toggle_status == 1 )
{
for( i = 0; i < count; i++ )
{
c[i]->select = NWIPE_SELECT_FALSE;
}
select_all_toggle_status = 0;
}
else
{
nwipe_log(
NWIPE_LOG_ERROR,
"gui.c:nwipe_gui_select(), Invalid value in variable select_all_toggle_status = %d",
select_all_toggle_status );
}
}
break;
} /* keystroke switch */
/* Check the terminal size, if the user has changed it the while loop checks for
* this change and exits the valid key hit loop so the windows can be updated */
getmaxyx( stdscr, stdscr_lines, stdscr_cols );
} /* key hit loop */
while( validkeyhit == 0 && terminate_signal != 1 && stdscr_cols_previous == stdscr_cols
&& stdscr_lines_previous == stdscr_lines );
} while( keystroke != 'S' && terminate_signal != 1 );
if( keystroke == 'S' )
{
/* If user has pressed S to start wipe change status line */
werase( footer_window );
nwipe_gui_title( footer_window, end_wipe_footer );
wnoutrefresh( footer_window );
}
} /* nwipe_gui_select */
void nwipe_gui_options( void )
{
/**
* Updates the options window.
*
* @modifies options_window
*
*/
/* Erase the window. */
werase( options_window );
mvwprintw(
options_window, NWIPE_GUI_OPTIONS_ENTROPY_Y, NWIPE_GUI_OPTIONS_ENTROPY_X, "Entropy: Linux Kernel (urandom)" );
mvwprintw(
options_window, NWIPE_GUI_OPTIONS_PRNG_Y, NWIPE_GUI_OPTIONS_PRNG_X, "PRNG: %s", nwipe_options.prng->label );
mvwprintw( options_window,
NWIPE_GUI_OPTIONS_METHOD_Y,
NWIPE_GUI_OPTIONS_METHOD_X,
"Method: %s",
nwipe_method_label( nwipe_options.method ) );
mvwprintw( options_window, NWIPE_GUI_OPTIONS_VERIFY_Y, NWIPE_GUI_OPTIONS_VERIFY_X, "Verify: " );
switch( nwipe_options.verify )
{
case NWIPE_VERIFY_NONE:
wprintw( options_window, "Off" );
break;
case NWIPE_VERIFY_LAST:
wprintw( options_window, "Last Pass" );
break;
case NWIPE_VERIFY_ALL:
wprintw( options_window, "All Passes" );
break;
default:
wprintw( options_window, "Unknown %i", nwipe_options.verify );
} /* switch verify */
mvwprintw( options_window, NWIPE_GUI_OPTIONS_ROUNDS_Y, NWIPE_GUI_OPTIONS_ROUNDS_X, "Rounds: " );
/* Disable blanking for ops2 and verify methods */
if( nwipe_options.method == &nwipe_ops2 || nwipe_options.method == &nwipe_verify )
{
nwipe_options.noblank = 1;
}
if( nwipe_options.noblank )
{
wprintw( options_window, "%i (no final blanking pass)", nwipe_options.rounds );
}
else
{
wprintw( options_window, "%i (plus blanking pass)", nwipe_options.rounds );
}
/* Add a border. */
box( options_window, 0, 0 );
/* Add a title. */
nwipe_gui_title( options_window, options_title );
/* Refresh the window. */
// wrefresh( options_window );
wnoutrefresh( options_window );
} /* nwipe_gui_options */
void nwipe_gui_rounds( void )
{
/**
* Allows the user to change the rounds option.
*
* @modifies nwipe_options.rounds
* @modifies main_window
*
*/
/* Set the initial focus. */
int focus = nwipe_options.rounds;
/* The first tabstop. */
const int tab1 = 2;
/* The current working row. */
int yy;
/* Input buffer. */
int keystroke;
extern int terminate_signal;
/* Update the footer window. */
werase( footer_window );
nwipe_gui_title( footer_window, rounds_footer );
wrefresh( footer_window );
do
{
/* Erase the main window. */
werase( main_window );
nwipe_gui_create_all_windows_on_terminal_resize( selection_footer );
/* Add a border. */
box( main_window, 0, 0 );
/* Add a title. */
nwipe_gui_title( main_window, " Rounds " );
/* Initialize the working row. */
yy = 4;
mvwprintw( main_window, yy++, tab1, "This is the number of times to run the wipe method on each device." );
mvwprintw( main_window, yy++, tab1, "" );
if( focus > 0 )
{
/* Print the syslinux configuration hint. */
mvwprintw( main_window, yy++, tab1, "syslinux.cfg: nuke=\"nwipe --rounds %i\"", focus );
/* Print this line last so that the cursor is in the right place. */
mvwprintw( main_window, 2, tab1, "> %i", focus );
}
else
{
mvwprintw( main_window, yy++, tab1, "The number of rounds must be a non-negative integer." );
/* Print this line last so that the cursor is in the right place. */
mvwprintw( main_window, 2, tab1, "> " );
}
/* Reveal the cursor. */
curs_set( 1 );
/* Refresh the window. */
wrefresh( main_window );
/* Wait 250ms for input from getch, if nothing getch will then continue,
* This is necessary so that the while loop can be exited by the
* terminate_signal e.g.. the user pressing control-c to exit.
* Do not change this value, a higher value means the keys become
* sluggish, any slower and more time is spent unnecessarily looping
* which wastes CPU cycles.
*/
timeout( 250 ); // block getch() for 250ms.
keystroke = getch(); // Get a keystroke.
timeout( -1 ); // Switch back to blocking mode.
switch( keystroke )
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if( focus < 100000000 )
{
/* Left shift, base ten. */
focus *= 10;
/* This assumes ASCII input, where the zero character is 0x30. */
focus += keystroke - 48;
}
break;
/* Escape key. */
case 27:
return;
case KEY_BACKSPACE:
case KEY_LEFT:
case 127:
/* Right shift, base ten. */
focus /= 10;
break;
} /* switch keystroke */
/* Hide the cursor. */
curs_set( 0 );
} while( keystroke != 10 && terminate_signal != 1 );
if( focus > 0 )
{
/* Set the number of rounds. */
nwipe_options.rounds = focus;
}
} /* nwipe_guid_rounds */
void nwipe_gui_prng( void )
{
/**
* Allows the user to change the PRNG.
*
* @modifies nwipe_options.prng
* @modifies main_window
*
*/
extern nwipe_prng_t nwipe_twister;
extern nwipe_prng_t nwipe_isaac;
extern int terminate_signal;
/* The number of implemented PRNGs. */
const int count = 2;
/* The first tabstop. */
const int tab1 = 2;
/* The second tabstop. */
const int tab2 = 30;
/* Set the initial focus. */
int focus = 0;
/* The current working row. */
int yy;
/* Input buffer. */
int keystroke;
/* Update the footer window. */
werase( footer_window );
nwipe_gui_title( footer_window, selection_footer );
wrefresh( footer_window );
if( nwipe_options.prng == &nwipe_twister )
{
focus = 0;
}
if( nwipe_options.prng == &nwipe_isaac )
{
focus = 1;
}
do
{
/* Clear the main window. */
werase( main_window );
nwipe_gui_create_all_windows_on_terminal_resize( selection_footer );
/* Initialize the working row. */
yy = 2;
/* Print the options. */
mvwprintw( main_window, yy++, tab1, "" );
mvwprintw( main_window, yy++, tab1, "" );
mvwprintw( main_window, yy++, tab1, " %s", nwipe_twister.label );
mvwprintw( main_window, yy++, tab1, " %s", nwipe_isaac.label );
mvwprintw( main_window, yy++, tab1, "" );
/* Print the cursor. */
mvwaddch( main_window, 4 + focus, tab1, ACS_RARROW );
switch( focus )
{
case 0:
mvwprintw( main_window, 2, tab2, "syslinux.cfg: nuke=\"nwipe --prng twister\"" );
mvwprintw( main_window,
yy++,
tab1,
"The Mersenne Twister, by Makoto Matsumoto and Takuji Nishimura, is a " );
mvwprintw( main_window,
yy++,
tab1,
"generalized feedback shift register PRNG that is uniform and " );
mvwprintw( main_window,
yy++,
tab1,
"equidistributed in 623-dimensions with a proven period of 2^19937-1. " );
mvwprintw( main_window,
yy++,
tab1,
" " );
mvwprintw( main_window,
yy++,
tab1,
"This implementation passes the Marsaglia Diehard test suite. " );
mvwprintw( main_window,
yy++,
tab1,
" " );
break;
case 1:
mvwprintw( main_window, 2, tab2, "syslinux.cfg: nuke=\"nwipe --prng isaac\"" );
mvwprintw( main_window,
yy++,
tab1,
"ISAAC, by Bob Jenkins, is a PRNG derived from RC4 with a minimum period of " );
mvwprintw( main_window,
yy++,
tab1,
"2^40 and an expected period of 2^8295. It is difficult to recover the " );
mvwprintw( main_window,
yy++,
tab1,
"initial PRNG state by cryptanalysis of the ISAAC stream. " );
mvwprintw( main_window,
yy++,
tab1,
" " );
break;
} /* switch */
/* Add a border. */
box( main_window, 0, 0 );
/* Add a title. */
nwipe_gui_title( main_window, " Pseudo Random Number Generator " );
/* Refresh the window. */
wrefresh( main_window );
/* Wait 250ms for input from getch, if nothing getch will then continue,
* This is necessary so that the while loop can be exited by the
* terminate_signal e.g.. the user pressing control-c to exit.
* Do not change this value, a higher value means the keys become
* sluggish, any slower and more time is spent unnecessarily looping
* which wastes CPU cycles.
*/
timeout( 250 ); // block getch() for 250ms.
keystroke = getch(); // Get a keystroke.
timeout( -1 ); // Switch back to blocking mode.
switch( keystroke )
{
case KEY_DOWN:
case 'j':
case 'J':
if( focus < count - 1 )
{
focus += 1;
}
break;
case KEY_UP:
case 'k':
case 'K':
if( focus > 0 )
{
focus -= 1;
}
break;
case KEY_ENTER:
case ' ':
case 10:
if( focus == 0 )
{
nwipe_options.prng = &nwipe_twister;
}
if( focus == 1 )
{
nwipe_options.prng = &nwipe_isaac;
}
return;
case KEY_BACKSPACE:
case KEY_BREAK:
return;
} /* switch */
} while( terminate_signal != 1 );
} /* nwipe_gui_prng */
void nwipe_gui_verify( void )
{
/**
* Allows the user to change the verification option.
*
* @modifies nwipe_options.verify
* @modifies main_window
*
*/
extern int terminate_signal;
/* The number of definitions in the nwipe_verify_t enumeration. */
const int count = 3;
/* The first tabstop. */
const int tab1 = 2;
/* The second tabstop. */
const int tab2 = 30;
/* Set the initial focus. */
int focus = nwipe_options.verify;
/* The current working row. */
int yy;
/* Input buffer. */
int keystroke;
/* Update the footer window. */
werase( footer_window );
nwipe_gui_title( footer_window, selection_footer );
wrefresh( footer_window );
do
{
nwipe_gui_create_all_windows_on_terminal_resize( selection_footer );
/* Clear the main window. */
werase( main_window );
/* Initialize the working row. */
yy = 2;
/* Print the options. */
mvwprintw( main_window, yy++, tab1, " Verification Off " );
mvwprintw( main_window, yy++, tab1, " Verify Last Pass " );
mvwprintw( main_window, yy++, tab1, " Verify All Passes " );
mvwprintw( main_window, yy++, tab1, " " );
/* Print the cursor. */
mvwaddch( main_window, 2 + focus, tab1, ACS_RARROW );
switch( focus )
{
case 0:
mvwprintw( main_window, 2, tab2, "syslinux.cfg: nuke=\"nwipe --verify off\"" );
mvwprintw( main_window,
yy++,
tab1,
"Do not verify passes. The wipe will be a write-only operation. " );
mvwprintw( main_window,
yy++,
tab1,
" " );
break;
case 1:
mvwprintw( main_window, 2, tab2, "syslinux.cfg: nuke=\"nwipe --verify last\"" );
mvwprintw( main_window,
yy++,
tab1,
"Check whether the device is actually empty after the last pass fills the " );
mvwprintw( main_window,
yy++,
tab1,
"device with zeros. " );
break;
case 2:
mvwprintw( main_window, 2, tab2, "syslinux.cfg: nuke=\"nwipe --verify all\"" );
mvwprintw( main_window,
yy++,
tab1,
"After every pass, read back the pattern and check whether it is correct. " );
mvwprintw( main_window,
yy++,
tab1,
" " );
mvwprintw( main_window,
yy++,
tab1,
"This program writes the entire length of the device before it reads back " );
mvwprintw( main_window,
yy++,
tab1,
"for verification, even for random pattern passes, to better ensure that " );
mvwprintw( main_window,
yy++,
tab1,
"hardware caches are actually flushed. " );
break;
} /* switch */
/* Add a border. */
box( main_window, 0, 0 );
/* Add a title. */
nwipe_gui_title( main_window, " Verification Mode " );
/* Refresh the window. */
wrefresh( main_window );
/* Wait 250ms for input from getch, if nothing getch will then continue,
* This is necessary so that the while loop can be exited by the
* terminate_signal e.g.. the user pressing control-c to exit.
* Do not change this value, a higher value means the keys become
* sluggish, any slower and more time is spent unnecessarily looping
* which wastes CPU cycles.
*/
timeout( 250 ); // block getch() for 250ms.
keystroke = getch(); // Get a keystroke.
timeout( -1 ); // Switch back to blocking mode.
switch( keystroke )
{
case KEY_DOWN:
case 'j':
case 'J':
if( focus < count - 1 )
{
focus += 1;
}
break;
case KEY_UP:
case 'k':
case 'K':
if( focus > 0 )
{
focus -= 1;
}
break;
case KEY_ENTER:
case ' ':
case 10:
if( focus >= 0 && focus < count )
{
nwipe_options.verify = focus;
}
if( nwipe_options.verify != NWIPE_VERIFY_NONE )
{
nwipe_options.noblank = 0;
}
return;
case KEY_BACKSPACE:
case KEY_BREAK:
return;
} /* switch */
} while( terminate_signal != 1 );
} /* nwipe_gui_verify */
void nwipe_gui_noblank( void )
{
/**
* Allows the user to change the verification option.
*
* @modifies nwipe_options.noblank
* @modifies main_window
*
*/
extern int terminate_signal;
/* The number of options available. */
const int count = 2;
/* The first tabstop. */
const int tab1 = 2;
/* The second tabstop. */
const int tab2 = 40;
/* Set the initial focus. */
int focus = nwipe_options.noblank;
/* The current working row. */
int yy;
/* Input buffer. */
int keystroke;
/* Update the footer window. */
werase( footer_window );
nwipe_gui_title( footer_window, selection_footer );
wrefresh( footer_window );
do
{
nwipe_gui_create_all_windows_on_terminal_resize( selection_footer );
/* Clear the main window. */
werase( main_window );
/* Initialize the working row. */
yy = 2;
/* Print the options. */
mvwprintw( main_window, yy++, tab1, " Perform a final blanking pass " );
mvwprintw( main_window, yy++, tab1, " Do not perform final blanking pass " );
mvwprintw( main_window, yy++, tab1, " " );
/* Print the cursor. */
mvwaddch( main_window, 2 + focus, tab1, ACS_RARROW );
switch( focus )
{
case 0:
mvwprintw( main_window, 2, tab2, "syslinux.cfg: nuke=\"nwipe\"" );
mvwprintw( main_window,
yy++,
tab1,
"Perform a final blanking pass after the wipe, leaving disk with only zeros. " );
mvwprintw( main_window,
yy++,
tab1,
"Note that the RCMP TSSIT OPS-II method never blanks the device regardless " );
mvwprintw( main_window,
yy++,
tab1,
"of this setting. " );
mvwprintw( main_window,
yy++,
tab1,
" " );
break;
case 1:
mvwprintw( main_window, 2, tab2, "syslinux.cfg: nuke=\"nwipe --noblank\"" );
mvwprintw( main_window,
yy++,
tab1,
"Do not perform a final blanking pass. Leave data as per final wiping pass. " );
mvwprintw( main_window,
yy++,
tab1,
"Note that the RCMP TSSIT OPS-II method never blanks the device regardless " );
mvwprintw( main_window,
yy++,
tab1,
"of this setting. " );
mvwprintw( main_window,
yy++,
tab1,
" " );
break;
} /* switch */
/* Add a border. */
box( main_window, 0, 0 );
/* Add a title. */
nwipe_gui_title( main_window, " Final Blanking Pass " );
/* Refresh the window. */
wrefresh( main_window );
/* Wait 250ms for input from getch, if nothing getch will then continue,
* This is necessary so that the while loop can be exited by the
* terminate_signal e.g.. the user pressing control-c to exit.
* Do not change this value, a higher value means the keys become
* sluggish, any slower and more time is spent unnecessarily looping
* which wastes CPU cycles.
*/
timeout( 250 ); // block getch() for 250ms.
keystroke = getch(); // Get a keystroke.
timeout( -1 ); // Switch back to blocking mode.
switch( keystroke )
{
case KEY_DOWN:
case 'j':
case 'J':
if( focus < count - 1 )
{
focus += 1;
}
break;
case KEY_UP:
case 'k':
case 'K':
if( focus > 0 )
{
focus -= 1;
}
break;
case KEY_ENTER:
case ' ':
case 10:
if( focus >= 0 && focus < count )
{
nwipe_options.noblank = focus;
}
return;
case KEY_BACKSPACE:
case KEY_BREAK:
return;
} /* switch */
}
while( terminate_signal != 1 );
} /* nwipe_gui_noblank */
void nwipe_gui_method( void )
{
/**
* Allows the user to change the wipe method.
*
* @modifies nwipe_options.method
* @modifies main_window
*
*/
extern int terminate_signal;
/* The number of implemented methods. */
const int count = 8;
/* The first tabstop. */
const int tab1 = 2;
/* The second tabstop. */
const int tab2 = 30;
/* The currently selected method. */
int focus = 0;
/* The current working row. */
int yy;
/* Input buffer. */
int keystroke;
/* Update the footer window. */
werase( footer_window );
nwipe_gui_title( footer_window, selection_footer );
wrefresh( footer_window );
if( nwipe_options.method == &nwipe_zero )
{
focus = 0;
}
if( nwipe_options.method == &nwipe_ops2 )
{
focus = 1;
}
if( nwipe_options.method == &nwipe_dodshort )
{
focus = 2;
}
if( nwipe_options.method == &nwipe_dod522022m )
{
focus = 3;
}
if( nwipe_options.method == &nwipe_gutmann )
{
focus = 4;
}
if( nwipe_options.method == &nwipe_random )
{
focus = 5;
}
if( nwipe_options.method == &nwipe_verify )
{
focus = 6;
}
if( nwipe_options.method == &nwipe_is5enh )
{
focus = 7;
}
do
{
/* Clear the main window. */
werase( main_window );
nwipe_gui_create_all_windows_on_terminal_resize( selection_footer );
/* Initialize the working row. */
yy = 2;
/* Print the options. */
mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_zero ) );
mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_ops2 ) );
mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_dodshort ) );
mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_dod522022m ) );
mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_gutmann ) );
mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_random ) );
mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_verify ) );
mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_is5enh ) );
mvwprintw( main_window, yy++, tab1, " " );
/* Print the cursor. */
mvwaddch( main_window, 2 + focus, tab1, ACS_RARROW );
switch( focus )
{
case 0:
mvwprintw( main_window, 2, tab2, "syslinux.cfg: nuke=\"nwipe --method zero\"" );
mvwprintw( main_window, 3, tab2, "Security Level: Low (1 pass)" );
mvwprintw( main_window,
yy++,
tab1,
"This method fills the device with zeros. Note that the rounds option does " );
mvwprintw( main_window,
yy++,
tab1,
"not apply to this method. This method always runs one round. " );
mvwprintw( main_window,
yy++,
tab1,
" " );
mvwprintw( main_window,
yy++,
tab1,
"Use this method to blank disks before internal redeployment, or before " );
mvwprintw( main_window,
yy++,
tab1,
"reinstalling Microsoft Windows to remove the data areas that the format " );
mvwprintw(
main_window, yy++, tab1, "utility preserves. " );
break;
case 1:
mvwprintw( main_window, 2, tab2, "syslinux.cfg: nuke=\"nwipe --method ops2\"" );
mvwprintw( main_window, 3, tab2, "Security Level: Medium (8 passes)" );
mvwprintw( main_window,
yy++,
tab1,
"The Royal Canadian Mounted Police Technical Security Standard for " );
mvwprintw( main_window,
yy++,
tab1,
"Information Technology, Appendix OPS-II: Media Sanitization. " );
mvwprintw( main_window,
yy++,
tab1,
" " );
mvwprintw( main_window,
yy++,
tab1,
"This implementation, with regards to paragraph 2 section A of the standard, " );
mvwprintw( main_window,
yy++,
tab1,
"uses a pattern that is one random byte and that is changed each round. " );
break;
case 2:
mvwprintw( main_window, 2, tab2, "syslinux.cfg: nuke=\"nwipe --method dodshort\"" );
mvwprintw( main_window, 3, tab2, "Security Level: Medium (3 passes)" );
mvwprintw( main_window,
yy++,
tab1,
"The American Department of Defense 5220.22-M short wipe. " );
mvwprintw( main_window,
yy++,
tab1,
"This method is composed of passes 1, 2 & 7 from the standard wipe. " );
break;
case 3:
mvwprintw( main_window, 2, tab2, "syslinux.cfg: nuke=\"nwipe --method dod522022m\"" );
mvwprintw( main_window, 3, tab2, "Security Level: Medium (7 passes)" );
mvwprintw( main_window,
yy++,
tab1,
"The American Department of Defense 5220.22-M standard wipe. " );
mvwprintw( main_window,
yy++,
tab1,
"This implementation uses the same algorithm as the Heidi Eraser product. " );
break;
case 4:
mvwprintw( main_window, 2, tab2, "syslinux.cfg: nuke=\"nwipe --method gutmann\"" );
mvwprintw( main_window, 3, tab2, "Security Level: High (35 passes)" );
mvwprintw( main_window,
yy++,
tab1,
"This is the method described by Peter Gutmann in the paper entitled " );
mvwprintw( main_window,
yy++,
tab1,
"\"Secure Deletion of Data from Magnetic and Solid-State Memory\". " );
break;
case 5:
mvwprintw( main_window, 2, tab2, "syslinux.cfg: nuke=\"nwipe --method random\"" );
mvwprintw( main_window, 3, tab2, "Security Level: Depends on Rounds" );
mvwprintw( main_window,
yy++,
tab1,
"This method fills the device with a stream from the PRNG. It is probably the " );
mvwprintw( main_window,
yy++,
tab1,
"best method to use on modern hard disk drives because encoding schemes vary. " );
mvwprintw( main_window,
yy++,
tab1,
" " );
mvwprintw( main_window,
yy++,
tab1,
"This method has a medium security level with 4 rounds, and a high security " );
mvwprintw( main_window,
yy++,
tab1,
"level with 8 rounds. " );
break;
case 6:
mvwprintw( main_window, 2, tab2, "syslinux.cfg: nuke=\"nwipe --method verify\"" );
mvwprintw( main_window, 3, tab2, "Security Level: None" );
mvwprintw( main_window,
yy++,
tab1,
"This method only reads the device and checks that it is all zero. " );
break;
case 7:
mvwprintw( main_window, 2, tab2, "syslinux.cfg: nuke=\"nwipe --method is5enh\"" );
mvwprintw( main_window, 3, tab2, "Security Level: Medium (3 passes)" );
mvwprintw( main_window,
yy++,
tab1,
"HMG IA/IS 5 (Infosec Standard 5): Secure Sanitisation of Protectively Marked " );
mvwprintw( main_window,
yy++,
tab1,
"Information or Sensitive Information " );
mvwprintw( main_window,
yy++,
tab1,
" " );
mvwprintw( main_window,
yy++,
tab1,
"This method fills the device with 0s, then with 1s, then with a PRNG stream, " );
mvwprintw( main_window,
yy++,
tab1,
"then reads the device to verify the PRNG stream was successfully written. " );
break;
} /* switch */
/* Add a border. */
box( main_window, 0, 0 );
/* Add a title. */
nwipe_gui_title( main_window, " Wipe Method " );
/* Refresh the window. */
wrefresh( main_window );
/* Wait 250ms for input from getch, if nothing getch will then continue,
* This is necessary so that the while loop can be exited by the
* terminate_signal e.g.. the user pressing control-c to exit.
* Do not change this value, a higher value means the keys become
* sluggish, any slower and more time is spent unnecessarily looping
* which wastes CPU cycles.
*/
timeout( 250 ); /* block getch() for 250ms */
keystroke = getch(); /* Get a keystroke. */
timeout( -1 ); /* Switch back to blocking mode */
switch( keystroke )
{
case KEY_DOWN:
case 'j':
case 'J':
if( focus < count - 1 )
{
focus += 1;
}
break;
case KEY_UP:
case 'k':
case 'K':
if( focus > 0 )
{
focus -= 1;
}
break;
case KEY_BACKSPACE:
case KEY_BREAK:
return;
} /* switch */
} while( keystroke != KEY_ENTER && keystroke != ' ' && keystroke != 10 && terminate_signal != 1 );
switch( focus )
{
case 0:
nwipe_options.method = &nwipe_zero;
break;
case 1:
nwipe_options.method = &nwipe_ops2;
break;
case 2:
nwipe_options.method = &nwipe_dodshort;
break;
case 3:
nwipe_options.method = &nwipe_dod522022m;
break;
case 4:
nwipe_options.method = &nwipe_gutmann;
break;
case 5:
nwipe_options.method = &nwipe_random;
break;
case 6:
nwipe_options.method = &nwipe_verify;
break;
case 7:
nwipe_options.method = &nwipe_is5enh;
break;
}
} /* nwipe_gui_method */
void nwipe_gui_load( void )
{
/**
* Prints the system load average to the statistics window.
*
* @modifies stat_window Prints the system load average to the statistics window.
*
*/
/* A file handle for the stat file. */
FILE* nwipe_fp;
/* The one, five, and fifteen minute load averages. */
float load_01;
float load_05;
float load_15;
/* Open the loadavg file. */
nwipe_fp = fopen( NWIPE_KNOB_LOADAVG, "r" );
/* Print the label. */
mvwprintw( stats_window, NWIPE_GUI_STATS_LOAD_Y, NWIPE_GUI_STATS_LOAD_X, "Load Averages:" );
if( nwipe_fp )
{
/* The load averages are the first three numbers in the file. */
if( 3 == fscanf( nwipe_fp, "%f %f %f", &load_01, &load_05, &load_15 ) )
{
/* Print the load average. */
mvwprintw( stats_window,
NWIPE_GUI_STATS_LOAD_Y,
NWIPE_GUI_STATS_TAB,
"%04.2f %04.2f %04.2f",
load_01,
load_05,
load_15 );
}
else
{
/* Print an error. */
mvwprintw( stats_window, NWIPE_GUI_STATS_LOAD_Y, NWIPE_GUI_STATS_TAB, "(fscanf error %i)", errno );
}
/* Close the loadavg file. */
fclose( nwipe_fp );
}
else
{
mvwprintw( stats_window, NWIPE_GUI_STATS_LOAD_Y, NWIPE_GUI_STATS_TAB, "(fopen error %i)", errno );
}
} /* nwipe_gui_load */
void* nwipe_gui_status( void* ptr )
{
/**
* Shows runtime statistics and overall progress.
*
* @parameter count The number of contexts in the array.
* @parameter c An array of device contexts.
*
* @modifies main_window Prints information into the main window.
* @modifies c[].throughput Updates the i/o throughput value.
*
*/
extern int terminate_signal;
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;
int count;
c = nwipe_thread_data_ptr->c;
nwipe_misc_thread_data = nwipe_thread_data_ptr->nwipe_misc_thread_data;
count = nwipe_misc_thread_data->nwipe_selected;
char nomenclature_result_str[NOMENCLATURE_RESULT_STR_SIZE]; /* temporary usage */
/* Spinner character */
char spinner_string[2];
/* We count time from when this function is first called. */
static time_t nwipe_time_start = 0;
/* Whether the screen has been blanked by the user. */
static int nwipe_gui_blank = 0;
/* The current time. */
time_t nwipe_time_now;
/* The time when all wipes ended */
time_t nwipe_time_stopped;
/* The index of the element that is visible in the first slot. */
static int offset;
/* The number of elements that we can show in the window. */
int slots;
/* Window dimensions. */
int wlines;
int wcols;
/* Generic loop variable. */
int i;
/* The current working line in the main window. */
int yy;
/* User input buffer. */
int keystroke;
/* controls main while loop */
int loop_control;
/* The combined througput of all processes. */
nwipe_misc_thread_data->throughput = 0;
/* The estimated runtime of the slowest device. */
nwipe_misc_thread_data->maxeta = 0;
/* The combined number of errors of all processes. */
nwipe_misc_thread_data->errors = 0;
/* Time values. */
int nwipe_hh;
int nwipe_mm;
int nwipe_ss;
struct timespec tim, tim2;
tim.tv_sec = 0;
tim.tv_nsec = 100000000L; /* sleep for 0.1 seconds */
/* Throughput variables */
u64 nwipe_throughput;
/* The number of active wipe processes. */
/* Set to 1 initially to start loop. */
int nwipe_active = 1;
if( nwipe_time_start == 0 )
{
/* This is the first time that we have been called. */
nwipe_time_start = time( NULL ) - 1;
}
nwipe_gui_title( footer_window, end_wipe_footer );
loop_control = 1;
while( loop_control )
{
/* IMPORTANT ! Halfdelay(1) causes getch() to pause for 0.1 secs. This is important for two reasons.
* 1. Pauses the getch for 0.1 secs so that the screen is only updated max 10 times/sec. Without
* this delay the loop would run hundreds of times per sec maxing out the core.
* 2. By keeping the delay below 0.2 seconds, i.e 0.1, it makes the keypress and resizing
* nice and responsive.
*/
halfdelay( 1 ); // Important, don't change this unless you know what you are doing ! Related to getch().
keystroke = getch(); // Get user input.
/* Get the current time. */
if( nwipe_active && terminate_signal != 1 )
{
nwipe_time_now = time( NULL );
nwipe_time_stopped = nwipe_time_now;
}
else
{
nwipe_time_now = nwipe_time_stopped;
}
/* Erase the main window. */
werase( main_window );
/* Erase the stats window. */
werase( stats_window );
/* Erase the footer window */
werase( footer_window );
/* Only repaint the windows on terminal resize if the user hasn't blanked the screen */
if( nwipe_gui_blank == 0 )
{
if( nwipe_active != 0 )
{
/* if resizing the terminal during a wipe a specific footer is required */
nwipe_gui_create_all_windows_on_terminal_resize( end_wipe_footer );
}
else
{
/* and if the wipes have finished a different footer is required */
nwipe_gui_create_all_windows_on_terminal_resize( wipes_finished_footer );
}
}
/* Initialize our working offset to the third line. */
yy = 2;
/* Get the window dimensions. */
getmaxyx( main_window, wlines, wcols );
/* Less four lines for the box and padding. */
slots = wlines - 4;
/* Each element prints three lines. */
slots /= 3;
if( nwipe_active == 0 || terminate_signal == 1 )
{
nwipe_gui_title( footer_window, wipes_finished_footer );
// Refresh the footer_window ;
wnoutrefresh( footer_window );
}
if( terminate_signal == 1 )
{
loop_control = 0;
}
if( keystroke > 0x0a && keystroke < 0x7e && nwipe_gui_blank == 1 )
{
/* Show screen */
nwipe_gui_blank = 0;
/* Set background */
wbkgdset( stdscr, COLOR_PAIR( 1 ) );
wclear( stdscr );
/* Unhide panels */
show_panel( header_panel );
show_panel( footer_panel );
show_panel( stats_panel );
show_panel( options_panel );
show_panel( main_panel );
/* Reprint the footer */
nwipe_gui_title( footer_window, end_wipe_footer );
// Refresh the footer_window ;
wnoutrefresh( footer_window );
/* Update panels */
update_panels();
doupdate();
}
else if( keystroke > 0 )
{
switch( keystroke )
{
case 'b':
case 'B':
/* Blank screen. */
nwipe_gui_blank = 1;
hide_panel( header_panel );
hide_panel( footer_panel );
hide_panel( stats_panel );
hide_panel( options_panel );
hide_panel( main_panel );
/* Set the background style. */
wbkgdset( stdscr, COLOR_PAIR( 7 ) );
wclear( stdscr );
break;
case KEY_DOWN:
case 'j':
case 'J':
/* Scroll down. */
offset += 1;
if( count < slots )
{
offset = 0;
}
else if( offset + slots > count )
{
offset = count - slots;
}
break;
case KEY_UP:
case 'k':
case 'K':
/* Scroll up. */
offset -= 1;
if( offset < 0 )
{
offset = 0;
}
break;
case ' ':
case 0x0a:
/* Check whether we have finished all wipes, if yes exit while loop if user pressed spacebar or
* return. */
if( !nwipe_active || terminate_signal == 1 )
{
loop_control = 0;
}
break;
default:
/* Do nothing. */
break;
}
} /* keystroke */
/* If wipe has completed and user has specified auto poweroff or nowait then we can skip waiting for the user to
* press return */
if( !nwipe_active )
{
if( nwipe_options.autopoweroff || nwipe_options.nowait )
{
loop_control = 0;
}
}
/* Update screen if not blanked. */
if( nwipe_gui_blank == 0 )
{
if( terminate_signal != 1 )
{
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 device label. */
mvwprintw( main_window, yy++, 2, "%s", c[i]->device_label );
/* Check whether the child process is still running the wipe. */
if( c[i]->wipe_status == 1 )
{
/* Print percentage and pass information. */
mvwprintw( main_window,
yy++,
4,
"[%5.2f%%, round %i of %i, pass %i of %i] ",
c[i]->round_percent,
c[i]->round_working,
c[i]->round_count,
c[i]->pass_working,
c[i]->pass_count );
} /* child running */
else
{
if( c[i]->result == 0 )
{
mvwprintw( main_window, yy++, 4, "[%05.2f%% complete, SUCCESS! ", c[i]->round_percent );
}
else if( c[i]->signal )
{
wattron( main_window, COLOR_PAIR( 9 ) );
mvwprintw( main_window, yy++, 4, "(>>> FAILURE! <<<, signal %i) ", c[i]->signal );
wattroff( main_window, COLOR_PAIR( 9 ) );
}
else
{
wattron( main_window, COLOR_PAIR( 9 ) );
mvwprintw( main_window, yy++, 4, "(>>>FAILURE!<<<, code %i) ", c[i]->result );
wattroff( main_window, COLOR_PAIR( 9 ) );
}
} /* child returned */
if( c[i]->verify_errors )
{
wprintw( main_window, "[verify errors: %llu] ", c[i]->verify_errors );
}
if( c[i]->pass_errors )
{
wprintw( main_window, "[pass errors: %llu] ", c[i]->pass_errors );
}
if( c[i]->wipe_status == 1 )
{
switch( c[i]->pass_type )
{
/* Each text field in square brackets should be the same number of characters
* to retain output in columns */
case NWIPE_PASS_FINAL_BLANK:
if( !c[i]->sync_status )
{
wprintw( main_window, "[ blanking] " );
}
break;
case NWIPE_PASS_FINAL_OPS2:
if( !c[i]->sync_status )
{
wprintw( main_window, "[OPS2final] " );
}
break;
case NWIPE_PASS_WRITE:
if( !c[i]->sync_status )
{
wprintw( main_window, "[ writing ] " );
}
break;
case NWIPE_PASS_VERIFY:
if( !c[i]->sync_status )
{
wprintw( main_window, "[verifying] " );
}
break;
case NWIPE_PASS_NONE:
break;
}
if( c[i]->sync_status )
{
wprintw( main_window, "[ syncing ] " );
}
}
/* Determine throughput nomenclature for this drive and output drives throughput to GUI */
Determine_C_B_nomenclature( c[i]->throughput, nomenclature_result_str, NOMENCLATURE_RESULT_STR_SIZE );
wprintw( main_window, "[%s/s] ", nomenclature_result_str );
/* Insert whitespace. */
yy += 1;
/* Increment the next spinner character for this context if the thread is active */
if( c[i]->wipe_status == 1 )
{
spinner( c, i );
spinner_string[0] = c[i]->spinner_character[0];
}
else
{
/* If the wipe thread is no longer active, replace the spinner with a space */
spinner_string[0] = ' ';
}
spinner_string[1] = 0;
wprintw( main_window, " %s ", spinner_string );
}
if( offset > 0 )
{
mvwprintw( main_window, 1, wcols - 8, " More " );
waddch( main_window, ACS_UARROW );
}
if( count - offset > slots )
{
mvwprintw( main_window, wlines - 2, wcols - 8, " More " );
waddch( main_window, ACS_DARROW );
}
/* Box the main window. */
box( main_window, 0, 0 );
/* Refresh the main window. */
wnoutrefresh( main_window );
/* Update the load average field, but only if we are still wiping */
if( nwipe_active && terminate_signal != 1 )
{
nwipe_gui_load();
}
nwipe_throughput = nwipe_misc_thread_data->throughput;
/* Determine the nomenclature for the combined throughput */
Determine_C_B_nomenclature( nwipe_throughput, nomenclature_result_str, NOMENCLATURE_RESULT_STR_SIZE );
/* Print the combined throughput. */
mvwprintw( stats_window, NWIPE_GUI_STATS_THROUGHPUT_Y, NWIPE_GUI_STATS_THROUGHPUT_X, "Throughput:" );
mvwprintw(
stats_window, NWIPE_GUI_STATS_THROUGHPUT_Y, NWIPE_GUI_STATS_TAB, "%s/s", nomenclature_result_str );
/* Change the current time into a delta. */
nwipe_time_now -= nwipe_time_start;
/* Put the delta into HH:mm:ss form. */
nwipe_hh = nwipe_time_now / 3600;
nwipe_time_now %= 3600;
nwipe_mm = nwipe_time_now / 60;
nwipe_time_now %= 60;
nwipe_ss = nwipe_time_now;
/* Print the runtime. */
mvwprintw( stats_window, NWIPE_GUI_STATS_RUNTIME_Y, 1, "Runtime:" );
mvwprintw( stats_window,
NWIPE_GUI_STATS_RUNTIME_Y,
NWIPE_GUI_STATS_TAB,
"%02i:%02i:%02i",
nwipe_hh,
nwipe_mm,
nwipe_ss );
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. */
nwipe_hh = nwipe_maxeta / 3600;
nwipe_maxeta %= 3600;
nwipe_mm = nwipe_maxeta / 60;
nwipe_maxeta %= 60;
nwipe_ss = nwipe_maxeta;
/* Print the estimated runtime remaining. */
mvwprintw( stats_window,
NWIPE_GUI_STATS_ETA_Y,
NWIPE_GUI_STATS_TAB,
"%02i:%02i:%02i",
nwipe_hh,
nwipe_mm,
nwipe_ss );
}
/* 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_misc_thread_data->errors );
/* Add a border. */
box( stats_window, 0, 0 );
/* Add a title. */
mvwprintw( stats_window, 0, ( NWIPE_GUI_STATS_W - strlen( stats_title ) ) / 2, "%s", stats_title );
/* Refresh internal representation of stats window */
wnoutrefresh( stats_window );
/* Output all windows to screen */
doupdate();
} // end blank screen if
} /* End of while loop */
if( nwipe_options.logfile[0] == '\0' )
{
nwipe_gui_title( footer_window, wipes_finished_footer );
}
else
{
char finish_message[NWIPE_GUI_FOOTER_W];
snprintf( finish_message,
sizeof( finish_message ),
"Wipe finished - press enter to exit. Logged to %s",
nwipe_options.logfile );
nwipe_gui_title( footer_window, finish_message );
}
terminate_signal = 1;
return NULL;
} /* 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 );
nwipe_misc_thread_data->throughput = 0;
nwipe_misc_thread_data->maxeta = 0;
nwipe_misc_thread_data->errors = 0;
/* 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]->wipe_status == 1 )
{
/* Increment the child counter. */
nwipe_active += 1;
/* Even if the wipe has finished ALWAYS run the stats one last time so the final SUCCESS percentage value is
* correct. 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 && c[i]->wipe_status == 1 )
{
/* 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;
}
}
/* Calculate the average throughput */
c[i]->throughput = (double) c[i]->round_done / (double) difftime( nwipe_time_now, c[i]->start_time );
}
/* Update the percentage value. */
c[i]->round_percent = (double) c[i]->round_done / (double) c[i]->round_size * 100;
if( c[i]->wipe_status == 1 )
{
/* Accumulate combined throughput. */
nwipe_misc_thread_data->throughput += c[i]->throughput;
}
/* 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 )
{
if( speedring->timeslast == 0 )
{
/* Ignore the first sample and initialize. */
speedring->timeslast = speedring_now;
return;
}
if( speedring_now - speedring->timeslast < NWIPE_KNOB_SPEEDRING_GRANULARITY )
{
/* Avoid jitter caused by frequent updates. */
return;
}
/* Subtract the oldest speed sample from the accumulator. */
speedring->bytestotal -= speedring->bytes[speedring->position];
speedring->timestotal -= speedring->times[speedring->position];
/* Put the lastest bytes-per-second sample into the ring buffer. */
speedring->bytes[speedring->position] = speedring_bytes - speedring->byteslast;
speedring->times[speedring->position] = speedring_now - speedring->timeslast;
/* Add the newest speed sample to the accumulator. */
speedring->bytestotal += speedring->bytes[speedring->position];
speedring->timestotal += speedring->times[speedring->position];
/* Remember the last sample. */
speedring->byteslast = speedring_bytes;
speedring->timeslast = speedring_now;
if( ++speedring->position >= NWIPE_KNOB_SPEEDRING_SIZE )
{
speedring->position = 0;
}
}
int spinner( nwipe_context_t** ptr, int device_idx )
{
nwipe_context_t** c;
c = ptr;
/* The spinner characters |/-\|/-\ */
char sc[9] = "|/-\\|/-\\/";
/* Check sanity of index */
if( c[device_idx]->spinner_idx < 0 || c[device_idx]->spinner_idx > 7 )
{
return 1;
}
c[device_idx]->spinner_character[0] = sc[c[device_idx]->spinner_idx];
c[device_idx]->spinner_idx++;
if( c[device_idx]->spinner_idx > 7 )
{
c[device_idx]->spinner_idx = 0;
}
return 0;
}