summaryrefslogtreecommitdiffstats
path: root/src/gui.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui.c')
-rw-r--r--src/gui.c3307
1 files changed, 3307 insertions, 0 deletions
diff --git a/src/gui.c b/src/gui.c
new file mode 100644
index 0000000..d0b05a0
--- /dev/null
+++ b/src/gui.c
@@ -0,0 +1,3307 @@
+/*
+ * 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"
+#include "temperature.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_footer_warning_no_blanking_with_ops2 =
+ " WARNING: Zero blanking is not allowed with ops2 method ";
+
+const char* main_window_footer_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=[Toggle between dark\\blank\\blue 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;
+
+int tft_saver = 0;
+
+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;
+ }
+
+ /* tft_saver = grey text on black mode */
+ if( tft_saver )
+ {
+ wattron( w, A_BOLD );
+ }
+
+ /* Print the title. */
+ mvwprintw( w, 0, margin / 2, "%s", s );
+
+} /* nwipe_gui_title */
+
+void nwipe_init_pairs( void )
+{
+ if( has_colors() )
+ {
+ /* Initialize color capabilities. */
+ start_color();
+
+ if( can_change_color() )
+ {
+ /* Redefine cyan to gray. */
+ init_color( COLOR_CYAN, 128, 128, 128 );
+ }
+
+ /* If we are in tft saver mode set grey text on black background else
+ * Set white on blue as the emphasis color */
+ if( tft_saver )
+ {
+ init_pair( 1, COLOR_BLACK, COLOR_BLACK );
+ }
+ else
+ {
+ 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 );
+
+ /* If we are in tft saver mode set grey text on black background else
+ * Set white on blue as the emphasis color */
+ if( tft_saver )
+ {
+ init_pair( 4, COLOR_BLACK, COLOR_BLACK );
+ }
+ else
+ {
+ 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 black on blue for minimum temperature reached */
+ init_pair( 11, COLOR_BLACK, COLOR_BLUE );
+
+ /* Set blue on blue to make temperature invisible */
+ init_pair( 12, COLOR_BLUE, COLOR_BLUE );
+
+ /* Set the background style. */
+ wbkgdset( stdscr, COLOR_PAIR( 1 ) | ' ' );
+ }
+}
+
+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 );
+
+ /* Create the text/background color pairs */
+ nwipe_init_pairs();
+
+ /* 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 ) );
+
+ /* In tft saver mode we toggle the intensity bit which gives us grey text */
+ if( tft_saver )
+ {
+ wattron( main_window, A_BOLD );
+ }
+ else
+ {
+ wattroff( main_window, A_BOLD );
+ }
+ }
+
+ /* 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()
+{
+ char anon_label[] = " (ANONYMIZED)";
+ char bannerplus[80];
+
+ /* 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 ) | ' ' );
+
+ if( tft_saver )
+ {
+ wattron( main_window, A_BOLD );
+ }
+ }
+
+ /* Clear the header window. */
+ werase( header_window );
+
+ /* If in anonymized mode modify the title banner to reflect this */
+ strcpy( bannerplus, banner );
+
+ if( nwipe_options.quiet )
+ {
+ strcat( bannerplus, anon_label );
+ }
+
+ /* Print the product banner. */
+ nwipe_gui_title( header_window, bannerplus );
+
+ /* 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 ) );
+
+ if( tft_saver )
+ {
+ wattron( options_window, A_BOLD );
+ }
+ }
+
+ /* 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 ) );
+
+ if( tft_saver )
+ {
+ wattron( stats_window, A_BOLD );
+ }
+ }
+
+ /* 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( int force_creation, 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 || force_creation == 1 )
+ {
+ /* 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;
+
+ time_t temperature_check_time = time( NULL );
+
+ /* Used in the selection loop to trap a failure of the timeout(), getch() mechanism to block for the designated
+ * period */
+ int iteration_counter;
+
+ /* Used in the selection loop to trap a failure of the timeout(), getch() mechanism to block for the designated
+ * period */
+ int expected_iterations;
+
+ time_t previous_iteration_timestamp;
+
+ do
+ {
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, 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:
+
+ if( nwipe_options.method == &nwipe_verify_zero || nwipe_options.method == &nwipe_verify_one )
+ {
+ wprintw( main_window,
+ "[vrfy] %s %s [%s] ",
+ c[i + offset]->gui_device_name,
+ c[i + offset]->device_type_str,
+ c[i + offset]->device_size_text );
+ }
+ else
+ {
+ wprintw( main_window,
+ "[wipe] %s %s [%s] ",
+ c[i + offset]->gui_device_name,
+ c[i + offset]->device_type_str,
+ c[i + offset]->device_size_text );
+ }
+ break;
+
+ case NWIPE_SELECT_FALSE:
+ /* Print an element that is not selected. */
+ wprintw( main_window,
+ "[ ] %s %s [%s] ",
+ c[i + offset]->gui_device_name,
+ c[i + offset]->device_type_str,
+ c[i + offset]->device_size_text );
+ break;
+
+ case NWIPE_SELECT_TRUE_PARENT:
+
+ /* This element will be wiped when its parent is wiped. */
+ wprintw( main_window,
+ "[****] %s %s [%s] ",
+ c[i + offset]->gui_device_name,
+ c[i + offset]->device_type_str,
+ c[i + offset]->device_size_text );
+ break;
+
+ case NWIPE_SELECT_FALSE_CHILD:
+
+ /* We can't wipe this element because it has a child that is being wiped. */
+ wprintw( main_window,
+ "[----] %s %s [%s] ",
+ c[i + offset]->gui_device_name,
+ c[i + offset]->device_type_str,
+ c[i + offset]->device_size_text );
+ 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 */
+
+ /* Read the drive temperature values */
+ nwipe_update_temperature( c[i + offset] );
+
+ /* print the temperature */
+ wprintw_temperature( c[i + offset] );
+
+ /* print the drive model and serial number */
+ wprintw( main_window, "%s/%s", c[i + offset]->device_model, c[i + offset]->device_serial_no );
+ }
+ 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();
+
+ /* Initialise the iteration counter */
+ iteration_counter = 0;
+
+ previous_iteration_timestamp = time( NULL );
+
+ /* Calculate Maximum allowed iterations per second */
+ expected_iterations = ( 1000 / GETCH_BLOCK_MS ) * 2;
+
+ do
+ {
+ /* 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.
+ */
+
+ validkeyhit = 0;
+ timeout( GETCH_BLOCK_MS ); // block getch() for ideally about 250ms.
+ keystroke = getch(); // Get user input.
+ timeout( -1 ); // Switch back to blocking mode.
+
+ /* To avoid 100% CPU usage, check for a runaway condition caused by the "keystroke = getch(); (above), from
+ * immediately returning an error condition. We check for an error condition because getch() returns a ERR
+ * value when the timeout value "timeout( 250 );" expires as well as when a real error occurs. We can't
+ * differentiate from normal operation and a failure of the getch function to block for the specified period
+ * of timeout. So here we check the while loop hasn't exceeded the number of expected iterations per second
+ * ie. a timeout(250) block value of 250ms means we should not see any more than (1000/250) = 4 iterations.
+ * We double this to 8 to allow a little tolerance. Why is this necessary? It's been found that in KDE
+ * konsole and other terminals based on the QT terminal engine exiting the terminal without first existing
+ * nwipe results in nwipe remaining running but detached from any interface which causes getch to fail and
+ * its associated timeout. So the CPU or CPU core rises to 100%. Here we detect that failure and exit nwipe
+ * gracefully with the appropriate error. This does not affect use of tmux for attaching or detaching from a
+ * running nwipe session when sitting at the selection screen. All other terminals correctly terminate nwipe
+ * when the terminal itself is exited.
+ */
+
+ iteration_counter++;
+
+ if( previous_iteration_timestamp == time( NULL ) )
+ {
+ if( iteration_counter > expected_iterations )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "GUI.c,nwipe_gui_select(), loop runaway, did you close the terminal without exiting "
+ "nwipe? Initiating shutdown now." );
+ /* Issue signal to nwipe to shutdown immediately but gracefully */
+ terminate_signal = 1;
+ }
+ }
+ else
+ {
+ /* new second, so reset counter */
+ iteration_counter = 0;
+ previous_iteration_timestamp = time( NULL );
+ }
+
+ /* 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_footer_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_zero || nwipe_options.method == &nwipe_verify_one )
+ {
+ /* 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_footer_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 );
+
+ /* Update the selection window every 60 seconds specifically so that the drive temperatures are updated */
+ if( time( NULL ) > ( temperature_check_time + 60 ) )
+ {
+ temperature_check_time = time( NULL );
+ validkeyhit = 1;
+ }
+
+ } /* 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_zero
+ || nwipe_options.method == &nwipe_verify_one )
+ {
+ 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( 0, 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." );
+ yy++;
+
+ if( focus > 0 )
+ {
+ /* 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 nwipe_prng_t nwipe_isaac64;
+ extern int terminate_signal;
+
+ /* The number of implemented PRNGs. */
+ const int count = 3;
+
+ /* 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;
+ }
+ if( nwipe_options.prng == &nwipe_isaac64 )
+ {
+ focus = 2;
+ }
+
+ do
+ {
+ /* Clear the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, selection_footer );
+
+ /* Initialize the working row. */
+ yy = 3;
+
+ /* Print the options. */
+ mvwprintw( main_window, yy++, tab1, " %s", nwipe_twister.label );
+ mvwprintw( main_window, yy++, tab1, " %s", nwipe_isaac.label );
+ mvwprintw( main_window, yy++, tab1, " %s", nwipe_isaac64.label );
+ yy++;
+
+ /* Print the cursor. */
+ mvwaddch( main_window, 3 + focus, tab1, ACS_RARROW );
+
+ switch( focus )
+ {
+ case 0:
+
+ 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,
+ 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,
+ " " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "Performs best on a 32-bit CPU. Use ISAAC-64 if this system has a 64-bit CPU." );
+ break;
+
+ case 2:
+
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "ISAAC-64, by Bob Jenkins, is like 32-bit ISAAC, but with a minimum period of" );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "2^77 and an expected period of 2^16583. It is difficult to recover the " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "initial PRNG state by cryptanalysis of the ISAAC-64 stream. " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ " " );
+ mvwprintw( main_window,
+ yy++,
+ tab1,
+ "Performs best on a 64-bit CPU. Use ISAAC if this system has a 32-bit CPU. " );
+ 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;
+ }
+ if( focus == 2 )
+ {
+ nwipe_options.prng = &nwipe_isaac64;
+ }
+ 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( 0, 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,
+ 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,
+ 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,
+ 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;
+ }
+ 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( 0, 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,
+ 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,
+ 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 = 10;
+
+ /* 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_one )
+ {
+ focus = 1;
+ }
+ if( nwipe_options.method == &nwipe_ops2 )
+ {
+ focus = 2;
+ }
+ if( nwipe_options.method == &nwipe_dodshort )
+ {
+ focus = 3;
+ }
+ if( nwipe_options.method == &nwipe_dod522022m )
+ {
+ focus = 4;
+ }
+ if( nwipe_options.method == &nwipe_gutmann )
+ {
+ focus = 5;
+ }
+ if( nwipe_options.method == &nwipe_random )
+ {
+ focus = 6;
+ }
+ if( nwipe_options.method == &nwipe_verify_zero )
+ {
+ focus = 7;
+ }
+ if( nwipe_options.method == &nwipe_verify_one )
+ {
+ focus = 8;
+ }
+ if( nwipe_options.method == &nwipe_is5enh )
+ {
+ focus = 9;
+ }
+
+ do
+ {
+ /* Clear the main window. */
+ werase( main_window );
+
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, 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_one ) );
+ 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_zero ) );
+ mvwprintw( main_window, yy++, tab1, " %s", nwipe_method_label( &nwipe_verify_one ) );
+ 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, "Security Level: high (1 pass)" );
+
+ mvwprintw( main_window, 4, tab2, "This method fills the device with zeros. Note " );
+ mvwprintw( main_window, 5, tab2, "that the rounds option does not apply to this " );
+ mvwprintw( main_window, 6, tab2, "method. This method always runs one round. " );
+ mvwprintw( main_window, 7, tab2, " " );
+ mvwprintw( main_window, 8, tab2, "There is no publicly available evidence that " );
+ mvwprintw( main_window, 9, tab2, "data can be recovered from a modern traditional " );
+ mvwprintw( main_window, 10, tab2, "hard drive (HDD) that has been zero wiped, " );
+ mvwprintw( main_window, 11, tab2, "however a wipe that includes a prng may be " );
+ mvwprintw( main_window, 12, tab2, "preferable. " );
+ break;
+
+ case 1:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: high (1 pass)" );
+
+ mvwprintw( main_window, 4, tab2, "This method fills the device with ones. Note that" );
+ mvwprintw( main_window, 5, tab2, "the rounds option does not apply to this method. " );
+ mvwprintw( main_window, 6, tab2, "This method always runs one round. " );
+ mvwprintw( main_window, 7, tab2, " " );
+ mvwprintw( main_window, 8, tab2, "This method might be used when wiping a solid " );
+ mvwprintw( main_window, 9, tab2, "state drive if an additional level of security is" );
+ mvwprintw( main_window, 10, tab2, "required beyond using the drives internal secure " );
+ mvwprintw( main_window, 11, tab2, "erase features. Alternatively PRNG may be " );
+ mvwprintw( main_window, 12, tab2, "preferable. " );
+ break;
+
+ case 2:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: higher (8 passes)" );
+
+ mvwprintw( main_window, 4, tab2, "The Royal Canadian Mounted Police Technical " );
+ mvwprintw( main_window, 5, tab2, "Security Standard for Information Technology. " );
+ mvwprintw( main_window, 6, tab2, "Appendix OPS-II: Media Sanitization. " );
+ mvwprintw( main_window, 7, tab2, " " );
+ mvwprintw( main_window, 8, tab2, "This implementation, with regards to paragraph 2 " );
+ mvwprintw( main_window, 9, tab2, "section A of the standard, uses a pattern that is" );
+ mvwprintw( main_window, 10, tab2, "one random byte and that is changed each round. " );
+ break;
+
+ case 3:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: higher (3 passes)" );
+
+ mvwprintw( main_window, 4, tab2, "The US Department of Defense 5220.22-M short wipe" );
+ mvwprintw( main_window, 5, tab2, "This method is composed of passes 1, 2 & 7 from " );
+ mvwprintw( main_window, 6, tab2, "the standard DoD 5220.22-M wipe. " );
+ mvwprintw( main_window, 7, tab2, " " );
+ mvwprintw( main_window, 8, tab2, "Pass 1: A random character " );
+ mvwprintw( main_window, 9, tab2, "Pass 2: The bitwise complement of pass 1. " );
+ mvwprintw( main_window, 10, tab2, "Pass 3: A random number generated data stream " );
+ break;
+
+ case 4:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: higher (7 passes)" );
+
+ mvwprintw( main_window, 3, tab2, "The American Department of Defense 5220.22-M " );
+ mvwprintw( main_window, 4, tab2, "standard wipe. " );
+ mvwprintw( main_window, 5, tab2, " " );
+ mvwprintw( main_window, 6, tab2, "Pass 1: A Random character " );
+ mvwprintw( main_window, 7, tab2, "Pass 2: The bitwise complement of pass 1 " );
+ mvwprintw( main_window, 8, tab2, "Pass 3: A random number generated data stream " );
+ mvwprintw( main_window, 9, tab2, "Pass 4: A Random character " );
+ mvwprintw( main_window, 10, tab2, "Pass 5: A Random character " );
+ mvwprintw( main_window, 11, tab2, "Pass 6: The bitwise complement of pass 5 " );
+ mvwprintw( main_window, 12, tab2, "Pass 7: A random number generated data stream " );
+ break;
+
+ case 5:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: Paranoid ! (35 passes) " );
+ mvwprintw( main_window, 3, tab2, "Don't waste your time with this on a modern drive" );
+
+ mvwprintw( main_window, 5, tab2, "This is the method described by Peter Gutmann in " );
+ mvwprintw( main_window, 6, tab2, "the paper entitled \"Secure Deletion of Data from" );
+ mvwprintw( main_window, 7, tab2, "Magnetic and Solid-State Memory\", however not " );
+ mvwprintw( main_window, 8, tab2, "relevant in regards to modern hard disk drives. " );
+ break;
+
+ case 6:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: Depends on Rounds" );
+
+ mvwprintw( main_window, 4, tab2, "This method fills the device with a stream from " );
+ mvwprintw( main_window, 5, tab2, "the PRNG. It is probably the best method to use " );
+ mvwprintw( main_window, 6, tab2, "on modern hard disk drives due to variation in " );
+ mvwprintw( main_window, 7, tab2, "encoding methods. " );
+ mvwprintw( main_window, 8, tab2, " " );
+ mvwprintw( main_window, 9, tab2, "This method has a high security level with 1 " );
+ mvwprintw( main_window, 10, tab2, "round and an increasingly higher security level " );
+ mvwprintw( main_window, 11, tab2, "as rounds are increased." );
+ break;
+
+ case 7:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: Not applicable" );
+
+ mvwprintw( main_window, 4, tab2, "This method only reads the device and checks " );
+ mvwprintw( main_window, 5, tab2, "that it is all zero. " );
+
+ break;
+
+ case 8:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: Not applicable" );
+
+ mvwprintw( main_window, 4, tab2, "This method only reads the device and checks " );
+ mvwprintw( main_window, 5, tab2, "that it is all ones (0xFF)." );
+
+ break;
+
+ case 9:
+
+ mvwprintw( main_window, 2, tab2, "Security Level: higher (3 passes)" );
+
+ mvwprintw( main_window, 4, tab2, "HMG IA/IS 5 (Infosec Standard 5): Secure " );
+ mvwprintw( main_window, 5, tab2, "Sanitisation of Protectively Marked Information " );
+ mvwprintw( main_window, 6, tab2, "or Sensitive Information " );
+ mvwprintw( main_window, 7, tab2, " " );
+ mvwprintw( main_window, 8, tab2, "This method fills the device with 0s, then with " );
+ mvwprintw( main_window, 9, tab2, "1s, then with a PRNG stream, then reads the " );
+ mvwprintw( main_window, 10, tab2, "device to verify the PRNG stream was " );
+ mvwprintw( main_window, 11, tab2, "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_one;
+ break;
+
+ case 2:
+ nwipe_options.method = &nwipe_ops2;
+ break;
+
+ case 3:
+ nwipe_options.method = &nwipe_dodshort;
+ break;
+
+ case 4:
+ nwipe_options.method = &nwipe_dod522022m;
+ break;
+
+ case 5:
+ nwipe_options.method = &nwipe_gutmann;
+ break;
+
+ case 6:
+ nwipe_options.method = &nwipe_random;
+ break;
+
+ case 7:
+ nwipe_options.method = &nwipe_verify_zero;
+ break;
+
+ case 8:
+ nwipe_options.method = &nwipe_verify_one;
+ break;
+
+ case 9:
+ 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;
+
+ /* Used in the gui status loop to trap a failure of the halfdelay(), getch() mechanism to block for the designated
+ * period */
+ int expected_iterations;
+
+ /* Used in the selection loop to trap a failure of the timeout(), getch() mechanism to block for the designated
+ * period, initialise the counter */
+ int iteration_counter = 0;
+
+ time_t previous_iteration_timestamp = time( NULL );
+
+ /* Calculate Maximum allowed iterations per second (typically 20), which is double the expected iterations
+ * (typically 10) */
+ expected_iterations = ( 1000 / GETCH_GUI_STATS_UPDATE_MS ) * 2;
+
+ 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( GETCH_GUI_STATS_UPDATE_MS ); // Important, don't change this unless you know what you are doing !
+ // Related to getch().
+
+ keystroke = getch(); // Get user input.
+
+ iteration_counter++;
+
+ /* Much like the same check we perform in the nwipe_gui_select() function, here we check that we are not looping
+ * any faster than as defined by the halfdelay() function above, typically this loop runs at 10 times a second.
+ * This check makes sure that if the loop runs faster than double this value i.e 20 times a second then the
+ * program exits. This check is therefore determining whether the getch() function is returning immediately
+ * rather than blocking for the defined period of 100ms. Why is this necessary? Some terminals (konsole &
+ * deriviatives) that are exited while nwipe is still running fail to terminate nwipe this causes the
+ * halfdelay()/getch() functions to immediately fail causing the loop frequency to drastically increase. We
+ * detect that speed increase here and therefore close down nwipe. This doesn't affect the use of the tmux
+ * terminal by which you can detach and reattach to running nwipe processes. tmux still works correctly.
+ */
+ if( previous_iteration_timestamp == time( NULL ) )
+ {
+ if( iteration_counter > expected_iterations )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "GUI.c,nwipe_gui_status(), loop runaway, did you close the terminal without exiting "
+ "nwipe? Initiating shutdown now." );
+ /* Issue signal to nwipe to shutdown immediately but gracefully */
+ terminate_signal = 1;
+ }
+ }
+ else
+ {
+ /* new second, so reset counter */
+ iteration_counter = 0;
+ previous_iteration_timestamp = time( NULL );
+ }
+
+ /* 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( 0, end_wipe_footer );
+ }
+ else
+ {
+ /* and if the wipes have finished a different footer is required */
+ nwipe_gui_create_all_windows_on_terminal_resize( 0, 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 )
+ {
+ tft_saver = 0;
+ nwipe_init_pairs();
+ nwipe_gui_create_all_windows_on_terminal_resize( 1, end_wipe_footer );
+
+ /* 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':
+
+ if( nwipe_gui_blank == 0 && tft_saver != 1 )
+ {
+ /* grey text on black background */
+ tft_saver = 1;
+ nwipe_init_pairs();
+ nwipe_gui_create_all_windows_on_terminal_resize( 1, end_wipe_footer );
+ }
+ else
+ {
+ if( nwipe_gui_blank == 0 && tft_saver == 1 )
+ {
+ /* Blank screen. */
+ tft_saver = 0;
+ 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 data in statistics & main windows only if we're in 'gui' mode and only if a wipe has started */
+ if( nwipe_gui_blank == 0 && global_wipe_status == 1 )
+ {
+
+ 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 details. */
+ mvwprintw( main_window,
+ yy++,
+ 2,
+ "%s %s [%s] ",
+ c[i]->device_name,
+ c[i]->device_type_str,
+ c[i]->device_size_text );
+ wprintw_temperature( c[i] );
+ wprintw( main_window, "%s/%s", c[i]->device_model, c[i]->device_serial_no );
+
+ /* 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, "[verr:%llu] ", c[i]->verify_errors );
+ }
+ if( c[i]->pass_errors )
+ {
+ wprintw( main_window, "[perr:%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;
+
+ /* Only update the estimated remaining runtime if the
+ * throughput for a given drive is greater than 100,000 bytes per second
+ * This prevents enormous ETA's being calculated on an unresponsive
+ * drive */
+ if( c[i]->throughput > 100000 )
+ {
+ 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;
+ nwipe_misc_thread_data->errors += c[i]->fsyncdata_errors;
+
+ /* Read the drive temperature values */
+ if( nwipe_time_now > ( c[i]->temp1_time + 60 ) )
+ {
+ nwipe_update_temperature( c[i] );
+ }
+
+ } /* 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 latest 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;
+}
+
+void temp1_flash( nwipe_context_t* c )
+{
+ if( c->temp1_flash_rate_counter < c->temp1_flash_rate )
+ {
+ c->temp1_flash_rate_counter++;
+ }
+ else
+ {
+ c->temp1_flash_rate_counter = 0;
+ if( c->temp1_flash_rate_status == 0 )
+ {
+ c->temp1_flash_rate_status = 1;
+ }
+ else
+ {
+ c->temp1_flash_rate_status = 0;
+ }
+ }
+}
+
+void wprintw_temperature( nwipe_context_t* c )
+{
+ /* If available, print the current temperature. This function determines
+ * whether the temperature should be printed as white, solid red, flashing red
+ * solid black or flashing black and then prints it to the main_window.
+ */
+ if( c->temp1_input != 1000000 )
+ {
+ /* if drive temperature has exceeded critical continuous running
+ * temperature && critical value is valid
+ */
+ if( c->temp1_input >= c->temp1_crit && c->temp1_crit != 1000000 )
+ {
+ temp1_flash( c );
+
+ if( c->temp1_flash_rate_status == 0 )
+ {
+ /* blue on blue */
+ wattron( main_window, COLOR_PAIR( 12 ) );
+ wprintw( main_window, "[%dC] ", c->temp1_input );
+ wattroff( main_window, COLOR_PAIR( 12 ) );
+ }
+ else
+ {
+ /* red on blue */
+ wattron( main_window, COLOR_PAIR( 3 ) );
+ wprintw( main_window, "[%dC] ", c->temp1_input );
+ wattroff( main_window, COLOR_PAIR( 3 ) );
+ }
+ }
+ else
+ {
+ /* if drive temperature has exceeded maximum continuous running
+ * temperature && max value is valid
+ */
+ if( c->temp1_input >= c->temp1_max && c->temp1_max != 1000000 )
+ {
+ /* red on blue */
+ wattron( main_window, COLOR_PAIR( 3 ) );
+ wprintw( main_window, "[%dC] ", c->temp1_input );
+ wattroff( main_window, COLOR_PAIR( 3 ) );
+ }
+ else
+ {
+ /* if drive temperature is below the critical temperature && critical value is valid */
+ if( c->temp1_input <= c->temp1_lcrit && c->temp1_lcrit != 1000000 )
+ {
+ temp1_flash( c );
+ if( c->temp1_flash_rate_status == 0 )
+ {
+ /* blue on blue */
+ wattron( main_window, COLOR_PAIR( 12 ) );
+ wprintw( main_window, "[%dC] ", c->temp1_input );
+ wattroff( main_window, COLOR_PAIR( 12 ) );
+ }
+ else
+ {
+ /* black on blue */
+ wattron( main_window, COLOR_PAIR( 11 ) );
+ wprintw( main_window, "[%dC] ", c->temp1_input );
+ wattroff( main_window, COLOR_PAIR( 11 ) );
+ }
+ }
+ else
+ {
+ /* if drive temperature is below the minimum continuous running temperature && minimum
+ * value is valid */
+ if( c->temp1_input <= c->temp1_min && c->temp1_min != 1000000 )
+ {
+ /* black on blue */
+ wattron( main_window, COLOR_PAIR( 11 ) );
+ wprintw( main_window, "[%dC] ", c->temp1_input );
+ wattroff( main_window, COLOR_PAIR( 11 ) );
+ }
+ else
+ {
+ /* Default white on blue */
+ wprintw( main_window, "[%dC] ", c->temp1_input );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ wprintw( main_window, "[--C] " );
+ }
+}