summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am10
-rw-r--r--src/context.h187
-rw-r--r--src/device.c829
-rw-r--r--src/device.h37
-rw-r--r--src/gui.c3307
-rw-r--r--src/gui.h62
-rw-r--r--src/isaac_rand/isaac64.c119
-rw-r--r--src/isaac_rand/isaac64.h41
-rw-r--r--src/isaac_rand/isaac_rand.c169
-rw-r--r--src/isaac_rand/isaac_rand.h52
-rw-r--r--src/isaac_rand/isaac_standard.h60
-rw-r--r--src/logging.c1007
-rw-r--r--src/logging.h52
-rw-r--r--src/method.c1375
-rw-r--r--src/method.h60
-rw-r--r--src/mt19937ar-cok/mt19937ar-cok.c139
-rw-r--r--src/mt19937ar-cok/mt19937ar-cok.h32
-rw-r--r--src/nwipe.c982
-rw-r--r--src/nwipe.h114
-rw-r--r--src/options.c596
-rw-r--r--src/options.h72
-rw-r--r--src/pass.c866
-rw-r--r--src/pass.h33
-rw-r--r--src/prng.c252
-rw-r--r--src/prng.h65
-rw-r--r--src/temperature.c270
-rw-r--r--src/temperature.h30
-rw-r--r--src/version.c17
-rw-r--r--src/version.h11
29 files changed, 10846 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..f4fea2f
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,10 @@
+# what flags you want to pass to the C compiler & linker
+#CFLAGS = -lncurses -lparted
+AM_CFLAGS =
+AM_LDFLAGS =
+
+# this lists the binaries to produce, the (non-PHONY, binary) targets in
+# the previous manual Makefile
+bin_PROGRAMS = nwipe
+nwipe_SOURCES = context.h logging.h options.h prng.h version.h temperature.h nwipe.c gui.c method.h pass.c device.c gui.h isaac_rand/isaac_standard.h isaac_rand/isaac_rand.h isaac_rand/isaac_rand.c isaac_rand/isaac64.h isaac_rand/isaac64.c mt19937ar-cok/mt19937ar-cok.c nwipe.h mt19937ar-cok/mt19937ar-cok.h pass.h device.h logging.c method.c options.c prng.c version.c temperature.c
+nwipe_LDADD = $(PARTED_LIBS)
diff --git a/src/context.h b/src/context.h
new file mode 100644
index 0000000..26e3432
--- /dev/null
+++ b/src/context.h
@@ -0,0 +1,187 @@
+/*
+ * context.h: The internal state representation of 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.
+ *
+ */
+
+#ifndef CONTEXT_H_
+#define CONTEXT_H_
+
+#include "prng.h"
+
+typedef enum nwipe_device_t_ {
+ NWIPE_DEVICE_UNKNOWN = 0, // Unknown device.
+ NWIPE_DEVICE_IDE,
+ NWIPE_DEVICE_SCSI,
+ NWIPE_DEVICE_COMPAQ, // Unimplemented.
+ NWIPE_DEVICE_USB,
+ NWIPE_DEVICE_IEEE1394, // Unimplemented.
+ NWIPE_DEVICE_ATA,
+ NWIPE_DEVICE_NVME,
+ NWIPE_DEVICE_VIRT,
+ NWIPE_DEVICE_SAS
+} nwipe_device_t;
+
+typedef enum nwipe_pass_t_ {
+ NWIPE_PASS_NONE = 0, // Not running.
+ NWIPE_PASS_WRITE, // Writing patterns to the device.
+ NWIPE_PASS_VERIFY, // Verifying a pass.
+ NWIPE_PASS_FINAL_BLANK, // Filling the device with zeros.
+ NWIPE_PASS_FINAL_OPS2 // Special case for nwipe_ops2.
+} nwipe_pass_t;
+
+typedef enum nwipe_select_t_ {
+ NWIPE_SELECT_NONE = 0, // Unused.
+ NWIPE_SELECT_TRUE, // Wipe this device.
+ NWIPE_SELECT_TRUE_PARENT, // A parent of this device has been selected, so the wipe is implied.
+ NWIPE_SELECT_FALSE, // Do not wipe this device.
+ NWIPE_SELECT_FALSE_CHILD, // A child of this device has been selected, so we can't wipe this device.
+ NWIPE_SELECT_DISABLED // Do not wipe this device and do not allow it to be selected.
+} nwipe_select_t;
+
+#define NWIPE_KNOB_SPEEDRING_SIZE 30
+#define NWIPE_KNOB_SPEEDRING_GRANULARITY 10
+
+typedef struct nwipe_speedring_t_
+{
+ u64 bytes[NWIPE_KNOB_SPEEDRING_SIZE];
+ u64 bytestotal;
+ u64 byteslast;
+ time_t times[NWIPE_KNOB_SPEEDRING_SIZE];
+ time_t timestotal;
+ time_t timeslast;
+ u32 position;
+} nwipe_speedring_t;
+
+#define NWIPE_DEVICE_LABEL_LENGTH 200
+#define NWIPE_DEVICE_SIZE_TXT_LENGTH 7
+
+// Arbitary length, so far most paths don't exceed about 25 characters
+#define MAX_HWMON_PATH_LENGTH 100
+
+typedef struct nwipe_context_t_
+{
+ /*
+ * Device fields
+ */
+ int device_block_size; // The soft block size reported by the device.
+ int device_sector_size; // The hard sector size reported by the device.
+ int device_bus; // The device bus number.
+ int device_fd; // The file descriptor of the device file being wiped.
+ int device_host; // The host number.
+ struct hd_driveid device_id; // The WIN_IDENTIFY data for IDE drives.
+ int device_lun; // The device logical unit number.
+ int device_major; // The major device number.
+ int device_minor; // The minor device number.
+ int device_part; // The device partition or slice number.
+ char* device_name; // The device file name.
+ char device_name_without_path[100];
+ char gui_device_name[100];
+ unsigned long long device_size; // The device size in bytes.
+ char* device_size_text; // The device size in a more (human)readable format.
+ char device_size_txt[NWIPE_DEVICE_SIZE_TXT_LENGTH]; // The device size in a more (human)readable format.
+ char* device_model; // The model of the device.
+ char device_label[NWIPE_DEVICE_LABEL_LENGTH]; // The label (name, model, size and serial) of the device.
+ struct stat device_stat; // The device file state from fstat().
+ nwipe_device_t device_type; // Indicates an IDE, SCSI, or Compaq SMART device in enumerated form (int)
+ char device_type_str[14]; // Indicates an IDE, SCSI, USB etc as per nwipe_device_t but in ascii
+ char device_serial_no[21]; // Serial number(processed, 20 characters plus null termination) of the device.
+ int device_target; // The device target.
+
+ u64 eta; // The estimated number of seconds until method completion.
+ int entropy_fd; // The entropy source. Usually /dev/urandom.
+ int pass_count; // The number of passes performed by the working wipe method.
+ u64 pass_done; // The number of bytes that have already been i/o'd in this pass.
+ u64 pass_errors; // The number of errors across all passes.
+ u64 pass_size; // The total number of i/o bytes across all passes.
+ nwipe_pass_t pass_type; // The type of the current working pass.
+ int pass_working; // The current working pass.
+ nwipe_prng_t* prng; // The PRNG implementation.
+ nwipe_entropy_t prng_seed; // The random data that is used to seed the PRNG.
+ void* prng_state; // The private internal state of the PRNG.
+ int result; // The process return value.
+ int round_count; // The number of rounds performed by the working wipe method.
+ u64 round_done; // The number of bytes that have already been i/o'd.
+ u64 round_errors; // The number of errors across all rounds.
+ u64 round_size; // The total number of i/o bytes across all rounds.
+ double round_percent; // The percentage complete across all rounds.
+ int round_working; // The current working round.
+ nwipe_select_t select; // Indicates whether this device should be wiped.
+ int signal; // Set when the child is killed by a signal.
+ nwipe_speedring_t speedring; // Ring buffer for computing the rolling throughput average.
+ short sync_status; // A flag to indicate when the method is syncing.
+ pthread_t thread; // The ID of the thread.
+ u64 throughput; // Average throughput in bytes per second.
+ u64 verify_errors; // The number of verification errors across all passes.
+ char temp1_path[MAX_HWMON_PATH_LENGTH]; // path to temperature variables /sys/class/hwmon/hwmonX/ etc.
+ int temp1_crit; // Critical high drive temperature, 1000000=unitialised, millidegree celsius.
+ int temp1_highest; // Historical highest temperature reached, 1000000=unitialised, millidegree celsius.
+ int temp1_input; // drive temperature, -1=unitialised. 1000000=unitialised, millidegree celsius.
+ int temp1_lcrit; // Critical low drive temperature, 1000000=unitialised, millidegree celsius.
+ int temp1_lowest; // Historically lowest temperature, 1000000=unitialised, millidegree celsius.
+ int temp1_max; // Maximum allowed temperature, 1000000=unitialised, millidegree celsius.
+ int temp1_min; // Miniumum allowed temperature, 1000000=unitialised, millidegree celsius.
+ int temp1_monitored_wipe_max;
+ int temp1_monitored_wipe_min;
+ int temp1_monitored_wipe_avg;
+ int temp1_flash_rate; // number relates to one tenth of a second, so 2 means a flash on and off = 0.4s
+ int temp1_flash_rate_counter; // used by the gui for timing the flash rate
+ int temp1_flash_rate_status; // 0=blank 1=visible
+ time_t temp1_time; // The time when temperature was last checked, seconds since epoch
+ int wipe_status; // Wipe finished = 0, wipe in progress = 1, wipe yet to start = -1.
+ int spinner_idx; // Index into the spinner character array
+ char spinner_character[1]; // The current spinner character
+ double duration; // Duration of the wipe in seconds
+ time_t start_time; // Start time of wipe
+ time_t end_time; // End time of wipe
+ u64 fsyncdata_errors; // The number of fsyncdata errors across all passes.
+ /*
+ * Identity contains the raw serial number of the drive
+ * (where applicable), however, for use within nwipe use the
+ * processed serial_no[21] string above. To access serial no. use
+ * c[i]->serial_no) and not c[i]->identity.serial_no);
+ */
+ struct hd_driveid identity;
+} nwipe_context_t;
+
+/*
+ * We use 2 data structs to pass data between threads.
+ * The first contains any required values.
+ * Values cannot form part of the second array below, hence the need for this.
+ */
+typedef struct
+{
+ int nwipe_enumerated; // The number of devices available.
+ int nwipe_selected; // The number of devices being wiped.
+ time_t maxeta; // The estimated runtime of the slowest device.
+ u64 throughput; // Total throughput.
+ u64 errors; // The combined number of errors of all processes.
+ pthread_t* gui_thread; // The ID of GUI thread.
+} nwipe_misc_thread_data_t;
+
+/*
+ * The second points to the first structure, as well as the structure of all the devices
+ */
+typedef struct
+{
+ nwipe_context_t** c; // Pointer to the nwipe context structure.
+ nwipe_misc_thread_data_t* nwipe_misc_thread_data; // Pointer to the misc structure above.
+} nwipe_thread_data_ptr_t;
+
+#endif /* CONTEXT_H_ */
diff --git a/src/device.c b/src/device.c
new file mode 100644
index 0000000..b7a87f5
--- /dev/null
+++ b/src/device.c
@@ -0,0 +1,829 @@
+/*
+ * device.c: Device routines 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.
+ *
+ */
+
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include "nwipe.h"
+#include "context.h"
+#include "device.h"
+#include "method.h"
+#include "options.h"
+#include "logging.h"
+#include <sys/ioctl.h>
+#include <linux/hdreg.h> // Drive specific defs
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+
+int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount );
+char* trim( char* str );
+
+extern int terminate_signal;
+
+int nwipe_device_scan( nwipe_context_t*** c )
+{
+ /**
+ * Scans the filesystem for storage device names.
+ *
+ * @parameter device_names A reference to a null array pointer.
+ * @modifies device_names Populates device_names with an array of nwipe_contect_t
+ * @returns The number of strings in the device_names array.
+ *
+ */
+
+ PedDevice* dev = NULL;
+ ped_device_probe_all();
+
+ int dcount = 0;
+
+ while( ( dev = ped_device_get_next( dev ) ) )
+ {
+ if( check_device( c, dev, dcount ) )
+ dcount++;
+
+ /* Don't bother scanning drives if the terminate signal is active ! as in the case of
+ * the readlink program missing which is required if the --nousb option has been specified */
+ if( terminate_signal == 1 )
+ {
+ break;
+ }
+ }
+
+ /* Return the number of devices that were found. */
+ return dcount;
+
+} /* nwipe_device_scan */
+
+int nwipe_device_get( nwipe_context_t*** c, char** devnamelist, int ndevnames )
+{
+ /**
+ * Gets information about devices
+ *
+ * @parameter device_names A reference to a null array pointer.
+ * @parameter devnamelist An array of string pointers to the device names
+ * @parameter ndevnames Number of elements in devnamelist
+ * @modifies device_names Populates device_names with an array of nwipe_contect_t
+ * @returns The number of strings in the device_names array.
+ *
+ */
+
+ PedDevice* dev = NULL;
+
+ int i;
+ int dcount = 0;
+
+ for( i = 0; i < ndevnames; i++ )
+ {
+
+ dev = ped_device_get( devnamelist[i] );
+ if( !dev )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Device %s not found", devnamelist[i] );
+ continue;
+ }
+
+ if( check_device( c, dev, dcount ) )
+ dcount++;
+
+ /* Don't bother scanning drives if the terminate signal is active ! as in the case of
+ * the readlink program missing which is required if the --nousb option has been specified */
+ if( terminate_signal == 1 )
+ {
+ break;
+ }
+ }
+
+ /* Return the number of devices that were found. */
+ return dcount;
+
+} /* nwipe_device_get */
+
+int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
+{
+ /* Populate this struct, then assign it to overall array of structs. */
+ nwipe_context_t* next_device;
+ int fd;
+ int idx;
+ int r;
+ char tmp_serial[21];
+ nwipe_device_t bus;
+
+ bus = 0;
+
+ /* Check whether this drive is on the excluded drive list ? */
+ idx = 0;
+ while( idx < 10 )
+ {
+ if( !strcmp( dev->path, nwipe_options.exclude[idx++] ) )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "Device %s excluded as per command line option -e", dev->path );
+ return 0;
+ }
+ }
+
+ /* Check whether the user has specified using the --nousb option
+ * that all USB devices should not be displayed or wiped whether
+ * in GUI, --nogui or --autonuke modes */
+
+ if( nwipe_options.nousb )
+ {
+ /* retrieve bus and drive serial number, HOWEVER we are only interested in the bus at this time */
+ r = nwipe_get_device_bus_type_and_serialno( dev->path, &bus, tmp_serial );
+
+ /* See nwipe_get_device_bus_type_and_serialno() function for meaning of these codes */
+ if( r == 0 || ( r >= 3 && r <= 6 ) )
+ {
+ if( bus == NWIPE_DEVICE_USB )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "Device %s ignored as per command line option --nousb", dev->path );
+ return 0;
+ }
+ }
+ else
+ {
+ if( r == 2 )
+ {
+ nwipe_log(
+ NWIPE_LOG_NOTICE, "--nousb requires the 'readlink' program, please install readlink", dev->path );
+ terminate_signal = 1;
+ return 0;
+ }
+ }
+ }
+
+ /* Try opening the device to see if it's valid. Close on completion. */
+ if( !ped_device_open( dev ) )
+ {
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to open device" );
+ return 0;
+ }
+ ped_device_close( dev );
+
+ /* New device, reallocate memory for additional struct pointer */
+ *c = realloc( *c, ( dcount + 1 ) * sizeof( nwipe_context_t* ) );
+
+ next_device = malloc( sizeof( nwipe_context_t ) );
+
+ /* Check the allocation. */
+ if( !next_device )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to create the array of enumeration contexts." );
+ return 0;
+ }
+
+ /* Zero the allocation. */
+ memset( next_device, 0, sizeof( nwipe_context_t ) );
+
+ /* Get device information */
+ next_device->device_model = dev->model;
+ remove_ATA_prefix( next_device->device_model );
+
+ /* full device name, i.e. /dev/sda */
+ next_device->device_name = dev->path;
+
+ /* remove /dev/ from device, right justify and prefix name so string length is eight characters */
+ nwipe_strip_path( next_device->device_name_without_path, next_device->device_name );
+
+ /* To maintain column alignment in the gui we have to remove /dev/ from device names that
+ * exceed eight characters including the /dev/ path.
+ */
+ if( strlen( next_device->device_name ) > MAX_LENGTH_OF_DEVICE_STRING )
+ {
+ strcpy( next_device->gui_device_name, next_device->device_name_without_path );
+ }
+ else
+ {
+ strcpy( next_device->gui_device_name, next_device->device_name );
+ }
+
+ next_device->device_size = dev->length * dev->sector_size;
+ Determine_C_B_nomenclature( next_device->device_size, next_device->device_size_txt, NWIPE_DEVICE_SIZE_TXT_LENGTH );
+ next_device->device_size_text = next_device->device_size_txt;
+ next_device->result = -2;
+
+ /* Attempt to get serial number of device.
+ */
+ next_device->device_serial_no[0] = 0; /* initialise the serial number */
+
+ if( ( fd = open( next_device->device_name = dev->path, O_RDONLY ) ) == ERR )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Unable to open device %s to obtain serial number", next_device->device_name );
+ }
+
+ /*
+ * We don't check the ioctl return status because there are plenty of situations where a serial number may not be
+ * returned by ioctl such as USB drives, logical volumes, encryted volumes, so the log file would have multiple
+ * benign ioctl errors reported which isn't necessarily a problem.
+ */
+ ioctl( fd, HDIO_GET_IDENTITY, &next_device->identity );
+ close( fd );
+
+ for( idx = 0; idx < 20; idx++ )
+ {
+ next_device->device_serial_no[idx] = next_device->identity.serial_no[idx];
+ }
+
+ // Terminate the string.
+ next_device->device_serial_no[20] = 0;
+
+ // Remove leading/trailing whitespace from serial number and left justify.
+ trim( (char*) next_device->device_serial_no );
+
+ /* if we couldn't obtain serial number by using the above method .. try this */
+ r = nwipe_get_device_bus_type_and_serialno( next_device->device_name, &next_device->device_type, tmp_serial );
+
+ /* If serial number & bus retrieved (0) OR unsupported USB bus identified (5) */
+ if( r == 0 || r == 5 )
+ {
+ /* If the serial number hasn't already been populated */
+ if( next_device->device_serial_no[0] == 0 )
+ {
+ strcpy( next_device->device_serial_no, tmp_serial );
+ }
+ }
+
+ /* Does the user want to anonymize serial numbers ? */
+ if( nwipe_options.quiet )
+ {
+ if( next_device->device_serial_no[0] == 0 )
+ {
+ strcpy( next_device->device_serial_no, "???????????????" );
+ }
+ else
+ {
+ strcpy( next_device->device_serial_no, "XXXXXXXXXXXXXXX" );
+ }
+ }
+
+ /* All device strings should be 4 characters, prefix with space if under 4 characters */
+ switch( next_device->device_type )
+ {
+ case NWIPE_DEVICE_UNKNOWN:
+ strcpy( next_device->device_type_str, " UNK" );
+ break;
+
+ case NWIPE_DEVICE_IDE:
+ strcpy( next_device->device_type_str, " IDE" );
+ break;
+
+ case NWIPE_DEVICE_SCSI:
+ strcpy( next_device->device_type_str, " SCSI" );
+ break;
+
+ case NWIPE_DEVICE_COMPAQ:
+ strcpy( next_device->device_type_str, " CPQ" );
+ break;
+
+ case NWIPE_DEVICE_USB:
+ strcpy( next_device->device_type_str, " USB" );
+ break;
+
+ case NWIPE_DEVICE_IEEE1394:
+ strcpy( next_device->device_type_str, "1394" );
+ break;
+
+ case NWIPE_DEVICE_ATA:
+ strcpy( next_device->device_type_str, " ATA" );
+ break;
+
+ case NWIPE_DEVICE_NVME:
+ strcpy( next_device->device_type_str, "NVME" );
+ break;
+
+ case NWIPE_DEVICE_VIRT:
+ strcpy( next_device->device_type_str, "VIRT" );
+ break;
+
+ case NWIPE_DEVICE_SAS:
+ strcpy( next_device->device_type_str, " SAS" );
+ break;
+ }
+
+ if( strlen( (const char*) next_device->device_serial_no ) )
+ {
+ snprintf( next_device->device_label,
+ NWIPE_DEVICE_LABEL_LENGTH,
+ "%s %s [%s] %s/%s",
+ next_device->device_name,
+ next_device->device_type_str,
+ next_device->device_size_text,
+ next_device->device_model,
+ next_device->device_serial_no );
+ }
+ else
+ {
+ snprintf( next_device->device_label,
+ NWIPE_DEVICE_LABEL_LENGTH,
+ "%s %s [%s] %s",
+ next_device->device_name,
+ next_device->device_type_str,
+ next_device->device_size_text,
+ next_device->device_model );
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "Found %s, %s, %s, %s, S/N=%s",
+ next_device->device_name,
+ next_device->device_type_str,
+ next_device->device_model,
+ next_device->device_size_text,
+ next_device->device_serial_no );
+
+ ( *c )[dcount] = next_device;
+ return 1;
+}
+
+/* Remove leading/trailing whitespace from a string and left justify result */
+char* trim( char* str )
+{
+ size_t len = 0;
+ char* frontp = str;
+ char* endp = NULL;
+
+ if( str == NULL )
+ {
+ return NULL;
+ }
+ if( str[0] == '\0' )
+ {
+ return str;
+ }
+ len = strlen( str );
+ endp = str + len;
+
+ /*
+ * Move the front and back pointers to address the first non-whitespace
+ * characters from each end.
+ */
+ while( isspace( (unsigned char) *frontp ) )
+ {
+ ++frontp;
+ }
+ if( endp != frontp )
+ {
+ while( isspace( (unsigned char) *( --endp ) ) && endp != frontp )
+ {
+ }
+ }
+ if( str + len - 1 != endp )
+ *( endp + 1 ) = '\0';
+ else if( frontp != str && endp == frontp )
+ *str = '\0';
+ /*
+ * Shift the string so that it starts at str so that if it's dynamically
+ * allocated, we can still free it on the returned pointer. Note the reuse
+ * of endp to mean the front of the string buffer now.
+ */
+ endp = str;
+ if( frontp != str )
+ {
+ while( *frontp )
+ {
+ *endp++ = *frontp++;
+ }
+ *endp = '\0';
+ }
+ return str;
+}
+
+int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, char* serialnumber )
+{
+ /* The caller provides a string that contains the device, i.e. /dev/sdc, also a pointer
+ * to a integer (bus type) and thirdly a 21 byte
+ * character string which this function populates with the serial number (20 characters + null terminator).
+ *
+ * The function populates the bus integer and serial number strings for the given device.
+ * Results for bus would typically be ATA or USB see nwipe_device_t in context.h
+ *
+ * Return Values:
+ * 0 = Success
+ * 1 = popen failed to create stream for readlink
+ * 2 = readlink exit code not 0, see nwipe logs
+ * 3 = popen failed to create stream for smartctl
+ * 4 = smartctl command not found, install smartmontools
+ * 5 = smartctl detected unsupported USB to IDE/SATA adapter
+ * 6 = All other errors !
+ *
+ */
+
+ FILE* fp;
+
+ int r; // A result buffer.
+ int idx_src;
+ int idx_dest;
+ int device_len;
+ int set_return_value;
+ int exit_status;
+ int idx;
+ int idx2;
+
+ char readlink_command[] = "readlink /sys/block/%s";
+ char readlink_command2[] = "/usr/bin/readlink /sys/block/%s";
+ char readlink_command3[] = "/sbin/readlink /sys/block/%s";
+ char smartctl_command[] = "smartctl -i %s";
+ char smartctl_command2[] = "/sbin/smartctl -i %s";
+ char smartctl_command3[] = "/usr/bin/smartctl -i %s";
+ char device_shortform[50];
+ char result[512];
+ char final_cmd_readlink[sizeof( readlink_command ) + sizeof( device_shortform )];
+ char final_cmd_smartctl[sizeof( smartctl_command ) + 256];
+ char* pResult;
+ char smartctl_labels_to_anonymize[][18] = {
+ "serial number:", "lu wwn device id:", "logical unit id:", "" /* Don't remove this empty string !, important */
+ };
+
+ /* Initialise return value */
+ set_return_value = 0;
+
+ *bus = 0;
+
+ /* Scan device name and if device is for instance /dev/sdx then convert to sdx
+ * If already sdx then just copy. */
+
+ idx_dest = 0;
+ device_shortform[idx_dest] = 0;
+ device_len = strlen( device );
+ idx_src = device_len;
+
+ while( idx_src >= 0 )
+ {
+ if( device[idx_src] == '/' || idx_src == 0 )
+ {
+ idx_src++;
+
+ /* Now scan forwards copying the short form device i.e sdc */
+ while( idx_src < device_len )
+ {
+ device_shortform[idx_dest++] = device[idx_src++];
+ }
+ break;
+ }
+ else
+ {
+ idx_src--;
+ }
+ }
+ device_shortform[idx_dest] = 0;
+
+ final_cmd_readlink[0] = 0;
+
+ /* Determine whether we can access readlink, required if the PATH environment is not setup ! (Debian sid 'su' as
+ * opposed to 'su -' */
+ if( system( "which readlink > /dev/null 2>&1" ) )
+ {
+ if( system( "which /sbin/readlink > /dev/null 2>&1" ) )
+ {
+ if( system( "which /usr/bin/readlink > /dev/null 2>&1" ) )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install readlink !" );
+ set_return_value = 2;
+
+ /* Return immediately if --nousb specified. Readlink is a requirement for this option. */
+ if( nwipe_options.nousb )
+ {
+ return set_return_value;
+ }
+ }
+ else
+ {
+ sprintf( final_cmd_readlink, readlink_command3, device_shortform );
+ }
+ }
+ else
+ {
+ sprintf( final_cmd_readlink, readlink_command2, device_shortform );
+ }
+ }
+ else
+ {
+ sprintf( final_cmd_readlink, readlink_command, device_shortform );
+ }
+
+ if( final_cmd_readlink[0] != 0 )
+ {
+
+ fp = popen( final_cmd_readlink, "r" );
+
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_WARNING,
+ "nwipe_get_device_bus_type_and_serialno: Failed to create stream to %s",
+ readlink_command );
+
+ set_return_value = 1;
+ }
+
+ if( fp != NULL )
+ {
+ /* Read the output a line at a time - output it. */
+ if( fgets( result, sizeof( result ) - 1, fp ) != NULL )
+ {
+ if( nwipe_options.verbose )
+ {
+ strip_CR_LF( result );
+ nwipe_log( NWIPE_LOG_DEBUG, "Readlink: %s", result );
+ }
+
+ /* Scan the readlink results for bus types, i.e. USB or ATA
+ * Example: readlink
+ * /sys/block/sdd../devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.3/2-1.3:1.0/host6/target6:0:0/6:0:0:0/block/sdd
+ */
+
+ if( strstr( result, "/usb" ) != 0 )
+ {
+ *bus = NWIPE_DEVICE_USB;
+ }
+ else
+ {
+ if( strstr( result, "/ata" ) != 0 )
+ {
+ *bus = NWIPE_DEVICE_ATA;
+ }
+ else
+ {
+ if( strstr( result, "/nvme/" ) != 0 )
+ {
+ *bus = NWIPE_DEVICE_NVME;
+ }
+ else
+ {
+ if( strstr( result, "/virtual/" ) != 0 )
+ {
+ *bus = NWIPE_DEVICE_VIRT;
+ }
+ }
+ }
+ }
+ }
+ /* close */
+ r = pclose( fp );
+
+ if( r > 0 )
+ {
+ exit_status = WEXITSTATUS( r );
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_WARNING,
+ "nwipe_get_device_bus_type_and_serialno(): readlink failed, \"%s\" exit status = %u",
+ final_cmd_readlink,
+ exit_status );
+ }
+
+ if( exit_status == 127 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install Readlink recommended !" );
+ set_return_value = 2;
+ if( nwipe_options.nousb )
+ {
+ return set_return_value;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Retrieve smartmontools drive information if USB bridge supports it, so we can retrieve the serial number of the
+ * drive that's on the other side of the USB bridge.. */
+
+ final_cmd_smartctl[0] = 0;
+
+ /* Determine whether we can access smartctl, required if the PATH environment is not setup ! (Debian sid 'su' as
+ * opposed to 'su -' */
+ if( system( "which smartctl > /dev/null 2>&1" ) )
+ {
+ if( system( "which /sbin/smartctl > /dev/null 2>&1" ) )
+ {
+ if( system( "which /usr/bin/smartctl > /dev/null 2>&1" ) )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install smartmontools !" );
+ }
+ else
+ {
+ sprintf( final_cmd_smartctl, smartctl_command3, device );
+ }
+ }
+ else
+ {
+ sprintf( final_cmd_smartctl, smartctl_command2, device );
+ }
+ }
+ else
+ {
+ sprintf( final_cmd_smartctl, smartctl_command, device );
+ }
+
+ if( final_cmd_smartctl[0] != 0 )
+ {
+ fp = popen( final_cmd_smartctl, "r" );
+
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_WARNING,
+ "nwipe_get_device_bus_type_and_serialno(): Failed to create stream to %s",
+ smartctl_command );
+
+ set_return_value = 3;
+ }
+ else
+ {
+ /* Read the output a line at a time - output it. */
+ while( fgets( result, sizeof( result ) - 1, fp ) != NULL )
+ {
+ /* Convert the label, i.e everything before the ':' to lower case, it's required to
+ * convert to lower case as smartctl seems to use inconsistent case when labeling
+ * for serial number, i.e mostly it produces labels "Serial Number:" but occasionaly
+ * it produces a label "Serial number:" */
+
+ idx = 0;
+ while( result[idx] != 0 && result[idx] != ':' )
+ {
+ /* If upper case alpha character, change to lower case */
+ if( result[idx] >= 'A' && result[idx] <= 'Z' )
+ {
+ result[idx] += 32;
+ }
+
+ idx++;
+ }
+
+ if( nwipe_options.verbose && result[0] != 0x0A )
+ {
+ strip_CR_LF( result );
+
+ /* Remove serial number if -q option specified */
+ if( nwipe_options.quiet )
+ {
+ /* initialise index into string array */
+ idx2 = 0;
+
+ while( smartctl_labels_to_anonymize[idx2][0] != 0 )
+ {
+ if( ( pResult = strstr( result, &smartctl_labels_to_anonymize[idx2][0] ) ) != 0 )
+ {
+ /* set index to character after end of label string */
+ idx = strlen( &smartctl_labels_to_anonymize[idx2][0] );
+
+ /* Ignore spaces, overwrite other characters */
+ while( *( pResult + idx ) != 0x0A && *( pResult + idx ) != 0x0D
+ && *( pResult + idx ) != 0 && idx <= sizeof( result ) - 1 )
+ {
+ if( *( pResult + idx ) == ' ' )
+ {
+ idx++;
+ continue;
+ }
+ else
+ {
+ /* ignore if the serial number has been written over with '?' */
+ if( *( pResult + idx ) != '?' )
+ {
+ *( pResult + idx ) = 'X';
+ }
+ idx++;
+ }
+ }
+ }
+ idx2++;
+ }
+ }
+
+ nwipe_log( NWIPE_LOG_DEBUG, "smartctl: %s", result );
+ }
+
+ if( strstr( result, "serial number:" ) != 0 )
+ {
+ /* strip any leading or trailing spaces and left justify, +15 is the length of "Serial Number:" */
+ trim( &result[15] );
+
+ strncpy( serialnumber, &result[15], 20 );
+ }
+
+ if( *bus == 0 )
+ {
+ if( strstr( result, "transport protocol:" ) != 0 )
+ {
+ /* strip any leading or trailing spaces and left justify, +4 is the length of "bus type:" */
+ trim( &result[19] );
+
+ if( strncmp( &result[19], "sas", 3 ) == 0 )
+ {
+ *bus = NWIPE_DEVICE_SAS;
+ }
+ }
+
+ if( strstr( result, "sata version is:" ) != 0 )
+ {
+
+ /* strip any leading or trailing spaces and left justify, +4 is the length of "bus type:" */
+ trim( &result[16] );
+
+ if( strncmp( &result[16], "sata", 4 ) == 0 )
+ {
+ *bus = NWIPE_DEVICE_ATA;
+ }
+ }
+ }
+ }
+
+ /* close */
+ r = pclose( fp );
+
+ if( r > 0 )
+ {
+ exit_status = WEXITSTATUS( r );
+ if( nwipe_options.verbose && exit_status != 1 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING,
+ "nwipe_get_device_bus_type_and_serialno(): smartctl failed, \"%s\" exit status = %u",
+ final_cmd_smartctl,
+ exit_status );
+ }
+ set_return_value = 6;
+
+ if( exit_status == 127 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install Smartctl recommended !" );
+
+ set_return_value = 4;
+ }
+
+ if( exit_status == 1 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "%s USB bridge, no pass-through support", device );
+
+ if( *bus == NWIPE_DEVICE_USB )
+ {
+ strcpy( serialnumber, "(S/N: unknown)" );
+ set_return_value = 5;
+ }
+ }
+ }
+ }
+ }
+
+ return set_return_value;
+}
+
+void strip_CR_LF( char* str )
+{
+ /* In the specified string, replace any CR or LF with a space */
+ int idx = 0;
+ int len = strlen( str );
+ while( idx < len )
+ {
+ if( str[idx] == 0x0A || str[idx] == 0x0D )
+ {
+ str[idx] = ' ';
+ }
+ idx++;
+ }
+}
+
+void remove_ATA_prefix( char* str )
+{
+ /* Remove "ATA" prefix if present in the model no. string, left justifing string */
+
+ int idx_pre = 4;
+ int idx_post = 0;
+
+ if( !strncmp( str, "ATA ", 4 ) )
+ {
+ while( str[idx_pre] != 0 )
+ {
+ str[idx_post++] = str[idx_pre++];
+ }
+
+ str[idx_post] = 0;
+ }
+}
diff --git a/src/device.h b/src/device.h
new file mode 100644
index 0000000..2a5c076
--- /dev/null
+++ b/src/device.h
@@ -0,0 +1,37 @@
+/*
+ * device.h: Device routines 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.
+ *
+ */
+
+#ifndef DEVICE_H_
+#define DEVICE_H_
+
+#define MAX_LENGTH_OF_DEVICE_STRING 8
+
+void nwipe_device_identify( nwipe_context_t* c ); // Get hardware information about the device.
+int nwipe_device_scan( nwipe_context_t*** c ); // Find devices that we can wipe.
+int nwipe_device_get( nwipe_context_t*** c, char** devnamelist, int ndevnames ); // Get info about devices to wipe.
+int nwipe_get_device_bus_type_and_serialno( char*, nwipe_device_t*, char* );
+void strip_CR_LF( char* );
+void determine_disk_capacity_nomenclature( u64, char* );
+void remove_ATA_prefix( char* );
+char* trim( char* );
+
+#endif /* DEVICE_H_ */
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] " );
+ }
+}
diff --git a/src/gui.h b/src/gui.h
new file mode 100644
index 0000000..29d00e6
--- /dev/null
+++ b/src/gui.h
@@ -0,0 +1,62 @@
+/*
+ * gui.h: 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.
+ *
+ */
+
+#ifndef GUI_H_
+#define GUI_H_
+
+void nwipe_gui_free( void ); // Stop the GUI.
+void nwipe_gui_init( void ); // Start the GUI.
+void nwipe_gui_create_main_window( void ); // Create the main window
+void nwipe_gui_create_header_window( void ); // Create the header window
+void nwipe_gui_create_footer_window( const char* ); // Create the footer window and write text
+void nwipe_gui_create_options_window( void ); // Create the options window
+void nwipe_gui_create_stats_window( void ); // Create the stats window
+void nwipe_gui_create_all_windows_on_terminal_resize(
+ int force_creation,
+ const char* footer_text ); // If terminal is resized recreate all windows
+void nwipe_gui_select( int count, nwipe_context_t** c ); // Select devices to wipe.
+void* nwipe_gui_status( void* ptr ); // Update operation progress.
+void nwipe_gui_method( void ); // Change the method option.
+void nwipe_gui_options( void ); // Update the options window.
+void nwipe_gui_prng( void ); // Change the prng option.
+void nwipe_gui_rounds( void ); // Change the rounds option.
+void nwipe_gui_verify( void ); // Change the verify option.
+void nwipe_gui_noblank( void ); // Change the noblank option.
+int spinner( nwipe_context_t** ptr, int ); // Return the next spinner character
+void temp1_flash( nwipe_context_t* ); // toggles term1_flash_status, which flashes the temperature
+void wprintw_temperature( nwipe_context_t* );
+
+int compute_stats( void* ptr );
+void nwipe_update_speedring( nwipe_speedring_t* speedring, u64 speedring_done, time_t speedring_now );
+
+#define NOMENCLATURE_RESULT_STR_SIZE 8
+
+/* Note Do not change unless you understand how this value affects keyboard response and screen refresh when
+ * the drive selection screen is displayed. (prior to wipe starting). */
+#define GETCH_BLOCK_MS 250 /* millisecond block time for getch() */
+
+/* Note The value of 1 (100ms) is the ideal speed for screen refresh during a wipe, a value of 2 is noticably slower,
+ * don't change unless you understand how this value affects keyboard responsiveness and speed of screen stats/spinner
+ * updating */
+#define GETCH_GUI_STATS_UPDATE_MS 1 /* 1 * 100 = 1/10/sec = millisecond block time for gui stats screen updates */
+
+#endif /* GUI_H_ */
diff --git a/src/isaac_rand/isaac64.c b/src/isaac_rand/isaac64.c
new file mode 100644
index 0000000..3e4203b
--- /dev/null
+++ b/src/isaac_rand/isaac64.c
@@ -0,0 +1,119 @@
+/*
+------------------------------------------------------------------------------
+isaac64.c: My random number generator for 64-bit machines.
+By Bob Jenkins, 1996. Public Domain.
+------------------------------------------------------------------------------
+*/
+#ifndef STANDARD
+#include "isaac_standard.h"
+#endif
+#ifndef ISAAC64
+#include "isaac64.h"
+#endif
+
+
+#define ind(mm,x) (*(ub8 *)((ub1 *)(mm) + ((x) & ((RANDSIZ-1)<<3))))
+#define rngstep(mix,a,b,mm,m,m2,r,x) \
+{ \
+ x = *m; \
+ a = (mix) + *(m2++); \
+ *(m++) = y = ind(mm,x) + a + b; \
+ *(r++) = b = ind(mm,y>>RANDSIZL) + x; \
+}
+
+void isaac64(rand64ctx *ctx)
+{
+ register ub8 a,b,x,y,*m,*mm,*m2,*r,*mend;
+ mm=ctx->mm; r=ctx->randrsl;
+ a = ctx->aa; b = ctx->bb + (++ctx->cc);
+ for (m = mm, mend = m2 = m+(RANDSIZ/2); m<mend; )
+ {
+ rngstep(~(a^(a<<21)), a, b, mm, m, m2, r, x);
+ rngstep( a^(a>>5) , a, b, mm, m, m2, r, x);
+ rngstep( a^(a<<12) , a, b, mm, m, m2, r, x);
+ rngstep( a^(a>>33) , a, b, mm, m, m2, r, x);
+ }
+ for (m2 = mm; m2<mend; )
+ {
+ rngstep(~(a^(a<<21)), a, b, mm, m, m2, r, x);
+ rngstep( a^(a>>5) , a, b, mm, m, m2, r, x);
+ rngstep( a^(a<<12) , a, b, mm, m, m2, r, x);
+ rngstep( a^(a>>33) , a, b, mm, m, m2, r, x);
+ }
+ ctx->bb = b; ctx->aa = a;
+}
+
+#define mix(a,b,c,d,e,f,g,h) \
+{ \
+ a-=e; f^=h>>9; h+=a; \
+ b-=f; g^=a<<9; a+=b; \
+ c-=g; h^=b>>23; b+=c; \
+ d-=h; a^=c<<15; c+=d; \
+ e-=a; b^=d>>14; d+=e; \
+ f-=b; c^=e<<20; e+=f; \
+ g-=c; d^=f>>17; f+=g; \
+ h-=d; e^=g<<14; g+=h; \
+}
+
+void rand64init(rand64ctx *ctx, word flag)
+{
+ word i;
+ ub8 a,b,c,d,e,f,g,h;
+ ub8 *mm, *randrsl;
+ ctx->aa = ctx->bb = ctx->cc = (ub8)0;
+ mm=ctx->mm;
+ randrsl=ctx->randrsl;
+ a=b=c=d=e=f=g=h=0x9e3779b97f4a7c13LL; /* the golden ratio */
+
+ for (i=0; i<4; ++i) /* scramble it */
+ {
+ mix(a,b,c,d,e,f,g,h);
+ }
+
+ for (i=0; i<RANDSIZ; i+=8) /* fill in mm[] with messy stuff */
+ {
+ if (flag) /* use all the information in the seed */
+ {
+ a+=randrsl[i ]; b+=randrsl[i+1]; c+=randrsl[i+2]; d+=randrsl[i+3];
+ e+=randrsl[i+4]; f+=randrsl[i+5]; g+=randrsl[i+6]; h+=randrsl[i+7];
+ }
+ mix(a,b,c,d,e,f,g,h);
+ mm[i ]=a; mm[i+1]=b; mm[i+2]=c; mm[i+3]=d;
+ mm[i+4]=e; mm[i+5]=f; mm[i+6]=g; mm[i+7]=h;
+ }
+
+ if (flag)
+ { /* do a second pass to make all of the seed affect all of mm */
+ for (i=0; i<RANDSIZ; i+=8)
+ {
+ a+=mm[i ]; b+=mm[i+1]; c+=mm[i+2]; d+=mm[i+3];
+ e+=mm[i+4]; f+=mm[i+5]; g+=mm[i+6]; h+=mm[i+7];
+ mix(a,b,c,d,e,f,g,h);
+ mm[i ]=a; mm[i+1]=b; mm[i+2]=c; mm[i+3]=d;
+ mm[i+4]=e; mm[i+5]=f; mm[i+6]=g; mm[i+7]=h;
+ }
+ }
+
+ isaac64(ctx); /* fill in the first set of results */
+ ctx->randcnt=RANDSIZ; /* prepare to use the first set of results */
+}
+
+#ifdef NEVER
+int main()
+{
+ ub8 i,j;
+ rand64ctx ctx;
+ ctx.aa=ctx.bb=ctx.cc=(ub8)0;
+ for (i=0; i<RANDSIZ; ++i) ctx.mm[i]=(ub8)0;
+ rand64init(&ctx, TRUE);
+ for (i=0; i<2; ++i)
+ {
+ isaac64(&ctx);
+ for (j=0; j<RANDSIZ; ++j)
+ {
+ printf("%.8lx%.8lx",(ub4)(ctx.randrsl[j]>>32),(ub4)ctx.randrsl[j]);
+ if ((j&3)==3) printf("\n");
+ }
+ }
+}
+#endif
diff --git a/src/isaac_rand/isaac64.h b/src/isaac_rand/isaac64.h
new file mode 100644
index 0000000..e2ab55b
--- /dev/null
+++ b/src/isaac_rand/isaac64.h
@@ -0,0 +1,41 @@
+/*
+------------------------------------------------------------------------------
+isaac64.h: definitions for a random number generator
+Bob Jenkins, 1996, Public Domain
+------------------------------------------------------------------------------
+*/
+#ifndef ISAAC64
+#define ISAAC64
+
+#include "isaac_standard.h"
+
+struct rand64ctx
+{
+ ub8 randrsl[RANDSIZ], randcnt;
+ ub8 mm[RANDSIZ];
+ ub8 aa, bb, cc;
+};
+typedef struct rand64ctx rand64ctx;
+
+/*
+------------------------------------------------------------------------------
+ If (flag==TRUE), then use the contents of randrsl[0..255] as the seed.
+------------------------------------------------------------------------------
+*/
+void rand64init(rand64ctx *r, word flag);
+
+void isaac64(rand64ctx *ctx);
+
+
+/*
+------------------------------------------------------------------------------
+ Call rand64() to retrieve a single 64-bit random value
+------------------------------------------------------------------------------
+*/
+#define isaac64_rand() \
+ (!(r)->randcnt-- ? \
+ (isaac64(r), (r)->randcnt=RANDSIZ-1, (r)->randrsl[(r)->>randcnt]) : \
+ (r)->randrsl[(r)->randcnt])
+
+#endif /* ISAAC64 */
+
diff --git a/src/isaac_rand/isaac_rand.c b/src/isaac_rand/isaac_rand.c
new file mode 100644
index 0000000..c9220e0
--- /dev/null
+++ b/src/isaac_rand/isaac_rand.c
@@ -0,0 +1,169 @@
+/*
+------------------------------------------------------------------------------
+rand.c: By Bob Jenkins. My random number generator, ISAAC. Public Domain.
+MODIFIED:
+ 960327: Creation (addition of randinit, really)
+ 970719: use context, not global variables, for internal state
+ 980324: added main (ifdef'ed out), also rearranged randinit()
+ 010626: Note that this is public domain
+------------------------------------------------------------------------------
+*/
+#ifndef STANDARD
+#include "isaac_standard.h"
+#endif
+#ifndef RAND
+#include "isaac_rand.h"
+#endif
+
+
+#define ind(mm,x) (*(ub4 *)((ub1 *)(mm) + ((x) & ((RANDSIZ-1)<<2))))
+#define rngstep(mix,a,b,mm,m,m2,r,x) \
+{ \
+ x = *m; \
+ a = (a^(mix)) + *(m2++); \
+ *(m++) = y = ind(mm,x) + a + b; \
+ *(r++) = b = ind(mm,y>>RANDSIZL) + x; \
+}
+
+void isaac(ctx)
+randctx *ctx;
+{
+ register ub4 a,b,x,y,*m,*mm,*m2,*r,*mend;
+ mm=ctx->randmem;
+ r=ctx->randrsl;
+ a = ctx->randa;
+ b = ctx->randb + (++ctx->randc);
+ for (m = mm, mend = m2 = m+(RANDSIZ/2); m<mend; )
+ {
+ rngstep( a<<13, a, b, mm, m, m2, r, x);
+ rngstep( a>>6 , a, b, mm, m, m2, r, x);
+ rngstep( a<<2 , a, b, mm, m, m2, r, x);
+ rngstep( a>>16, a, b, mm, m, m2, r, x);
+ }
+ for (m2 = mm; m2<mend; )
+ {
+ rngstep( a<<13, a, b, mm, m, m2, r, x);
+ rngstep( a>>6 , a, b, mm, m, m2, r, x);
+ rngstep( a<<2 , a, b, mm, m, m2, r, x);
+ rngstep( a>>16, a, b, mm, m, m2, r, x);
+ }
+ ctx->randb = b; ctx->randa = a;
+}
+
+
+#define mix(a,b,c,d,e,f,g,h) \
+{ \
+ a^=b<<11; d+=a; b+=c; \
+ b^=c>>2; e+=b; c+=d; \
+ c^=d<<8; f+=c; d+=e; \
+ d^=e>>16; g+=d; e+=f; \
+ e^=f<<10; h+=e; f+=g; \
+ f^=g>>4; a+=f; g+=h; \
+ g^=h<<8; b+=g; h+=a; \
+ h^=a>>9; c+=h; a+=b; \
+}
+
+/* if (flag==TRUE), then use the contents of randrsl[] to initialize mm[]. */
+void randinit(ctx, flag)
+randctx *ctx;
+word flag;
+{
+ word i;
+ ub4 a,b,c,d,e,f,g,h;
+ ub4 *m,*r;
+ ctx->randa = ctx->randb = ctx->randc = 0;
+ m=ctx->randmem;
+ r=ctx->randrsl;
+ a=b=c=d=e=f=g=h=0x9e3779b9; /* the golden ratio */
+
+ for (i=0; i<4; ++i) /* scramble it */
+ {
+ mix(a,b,c,d,e,f,g,h);
+ }
+
+ if (flag)
+ {
+ /* initialize using the contents of r[] as the seed */
+ for (i=0; i<RANDSIZ; i+=8)
+ {
+ a+=r[i ];
+ b+=r[i+1];
+ c+=r[i+2];
+ d+=r[i+3];
+ e+=r[i+4];
+ f+=r[i+5];
+ g+=r[i+6];
+ h+=r[i+7];
+ mix(a,b,c,d,e,f,g,h);
+ m[i ]=a;
+ m[i+1]=b;
+ m[i+2]=c;
+ m[i+3]=d;
+ m[i+4]=e;
+ m[i+5]=f;
+ m[i+6]=g;
+ m[i+7]=h;
+ }
+ /* do a second pass to make all of the seed affect all of m */
+ for (i=0; i<RANDSIZ; i+=8)
+ {
+ a+=m[i ];
+ b+=m[i+1];
+ c+=m[i+2];
+ d+=m[i+3];
+ e+=m[i+4];
+ f+=m[i+5];
+ g+=m[i+6];
+ h+=m[i+7];
+ mix(a,b,c,d,e,f,g,h);
+ m[i ]=a;
+ m[i+1]=b;
+ m[i+2]=c;
+ m[i+3]=d;
+ m[i+4]=e;
+ m[i+5]=f;
+ m[i+6]=g;
+ m[i+7]=h;
+ }
+ }
+ else
+ {
+ /* fill in m[] with messy stuff */
+ for (i=0; i<RANDSIZ; i+=8)
+ {
+ mix(a,b,c,d,e,f,g,h);
+ m[i ]=a;
+ m[i+1]=b;
+ m[i+2]=c;
+ m[i+3]=d;
+ m[i+4]=e;
+ m[i+5]=f;
+ m[i+6]=g;
+ m[i+7]=h;
+ }
+ }
+
+ isaac(ctx); /* fill in the first set of results */
+ ctx->randcnt=RANDSIZ; /* prepare to use the first set of results */
+}
+
+
+#ifdef NEVER
+int main()
+{
+ ub4 i,j;
+ randctx ctx;
+ ctx.randa=ctx.randb=ctx.randc=(ub4)0;
+ for (i=0; i<256; ++i) ctx.randrsl[i]=(ub4)0;
+ randinit(&ctx, TRUE);
+ for (i=0; i<2; ++i)
+ {
+ isaac(&ctx);
+ for (j=0; j<256; ++j)
+ {
+ printf("%.8lx",ctx.randrsl[j]);
+ if ((j&7)==7) printf("\n");
+ }
+ }
+}
+#endif
diff --git a/src/isaac_rand/isaac_rand.h b/src/isaac_rand/isaac_rand.h
new file mode 100644
index 0000000..167a2fd
--- /dev/null
+++ b/src/isaac_rand/isaac_rand.h
@@ -0,0 +1,52 @@
+/*
+------------------------------------------------------------------------------
+rand.h: definitions for a random number generator
+By Bob Jenkins, 1996, Public Domain
+MODIFIED:
+ 960327: Creation (addition of randinit, really)
+ 970719: use context, not global variables, for internal state
+ 980324: renamed seed to flag
+ 980605: recommend RANDSIZL=4 for noncryptography.
+ 010626: note this is public domain
+------------------------------------------------------------------------------
+*/
+#ifndef RAND
+#define RAND
+
+#include "isaac_standard.h"
+
+/* context of random number generator */
+struct randctx
+{
+ ub4 randcnt;
+ ub4 randrsl[RANDSIZ];
+ ub4 randmem[RANDSIZ];
+ ub4 randa;
+ ub4 randb;
+ ub4 randc;
+};
+typedef struct randctx randctx;
+
+/*
+------------------------------------------------------------------------------
+ If (flag==TRUE), then use the contents of randrsl[0..RANDSIZ-1] as the seed.
+------------------------------------------------------------------------------
+*/
+void randinit(/*_ randctx *r, word flag _*/);
+
+void isaac(/*_ randctx *r _*/);
+
+
+/*
+------------------------------------------------------------------------------
+ Call rand(/o_ randctx *r _o/) to retrieve a single 32-bit random value
+------------------------------------------------------------------------------
+*/
+#define isaac_rand(r) \
+ (!(r)->randcnt-- ? \
+ (isaac(r), (r)->randcnt=RANDSIZ-1, (r)->randrsl[(r)->randcnt]) : \
+ (r)->randrsl[(r)->randcnt])
+
+#endif /* RAND */
+
+
diff --git a/src/isaac_rand/isaac_standard.h b/src/isaac_rand/isaac_standard.h
new file mode 100644
index 0000000..5323d3f
--- /dev/null
+++ b/src/isaac_rand/isaac_standard.h
@@ -0,0 +1,60 @@
+/*
+------------------------------------------------------------------------------
+Standard definitions and types, Bob Jenkins
+------------------------------------------------------------------------------
+*/
+#ifndef STANDARD
+# define STANDARD
+# ifndef STDIO
+# include <stdio.h>
+# define STDIO
+# endif
+# ifndef STDDEF
+# include <stddef.h>
+# define STDDEF
+# endif
+typedef unsigned long long ub8;
+#define UB8MAXVAL 0xffffffffffffffffLL
+#define UB8BITS 64
+typedef signed long long sb8;
+#define SB8MAXVAL 0x7fffffffffffffffLL
+typedef unsigned long int ub4; /* unsigned 4-byte quantities */
+#define UB4MAXVAL 0xffffffff
+typedef signed long int sb4;
+#define UB4BITS 32
+#define SB4MAXVAL 0x7fffffff
+typedef unsigned short int ub2;
+#define UB2MAXVAL 0xffff
+#define UB2BITS 16
+typedef signed short int sb2;
+#define SB2MAXVAL 0x7fff
+typedef unsigned char ub1;
+#define UB1MAXVAL 0xff
+#define UB1BITS 8
+typedef signed char sb1; /* signed 1-byte quantities */
+#define SB1MAXVAL 0x7f
+typedef int word; /* fastest type available */
+
+#define bis(target,mask) ((target) |= (mask))
+#define bic(target,mask) ((target) &= ~(mask))
+#define bit(target,mask) ((target) & (mask))
+#ifndef min
+# define min(a,b) (((a)<(b)) ? (a) : (b))
+#endif /* min */
+#ifndef max
+# define max(a,b) (((a)<(b)) ? (b) : (a))
+#endif /* max */
+#ifndef align
+# define align(a) (((ub4)a+(sizeof(void *)-1))&(~(sizeof(void *)-1)))
+#endif /* align */
+#ifndef abs
+# define abs(a) (((a)>0) ? (a) : -(a))
+#endif
+#define TRUE 1
+#define FALSE 0
+#define SUCCESS 0 /* 1 on VAX */
+
+#define RANDSIZL (8) /* I recommend 8 for crypto, 4 for simulations */
+#define RANDSIZ (1<<RANDSIZL)
+
+#endif /* STANDARD */
diff --git a/src/logging.c b/src/logging.c
new file mode 100644
index 0000000..1ff3ed1
--- /dev/null
+++ b/src/logging.c
@@ -0,0 +1,1007 @@
+/*
+ * logging.c: Logging facilities for nwipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.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.
+ *
+ */
+
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+
+#ifndef _POSIX_SOURCE
+#define _POSIX_SOURCE
+#endif
+
+#include "stdio.h"
+#include "stdlib.h"
+#include "string.h"
+#include "stdarg.h"
+#include "nwipe.h"
+#include "context.h"
+#include "method.h"
+#include "prng.h"
+#include "options.h"
+#include "logging.h"
+
+/* Global array to hold log values to print when logging to STDOUT */
+char** log_lines;
+int log_current_element = 0;
+int log_elements_allocated = 0;
+int log_elements_displayed = 0;
+pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
+
+void nwipe_log( nwipe_log_t level, const char* format, ... )
+{
+ /**
+ * Writes a message to the program log file.
+ *
+ */
+
+ extern int terminate_signal;
+ extern int user_abort;
+
+ char** result;
+ char* malloc_result;
+ char message_buffer[MAX_LOG_LINE_CHARS * sizeof( char )];
+ int chars_written;
+
+ int message_buffer_length;
+ int r; /* result buffer */
+
+ /* A time buffer. */
+ time_t t;
+
+ /* A pointer to the system time struct. */
+ struct tm* p;
+ r = pthread_mutex_lock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_lock failed. Code %i \n", r );
+ return;
+ }
+
+ /* Get the current time. */
+ t = time( NULL );
+ p = localtime( &t );
+
+ /* Position of writing to current log string */
+ int line_current_pos = 0;
+
+ /* initialise characters written */
+ chars_written = 0;
+
+ /* Print the date. The rc script uses the same format. */
+ if( level != NWIPE_LOG_NOTIMESTAMP )
+ {
+ chars_written = snprintf( message_buffer,
+ MAX_LOG_LINE_CHARS,
+ "[%i/%02i/%02i %02i:%02i:%02i] ",
+ 1900 + p->tm_year,
+ 1 + p->tm_mon,
+ p->tm_mday,
+ p->tm_hour,
+ p->tm_min,
+ p->tm_sec );
+ }
+
+ /*
+ * Has the end of the buffer been reached ?, snprintf returns the number of characters that would have been
+ * written if MAX_LOG_LINE_CHARS had not been reached, it does not return the actual characters written in
+ * all circumstances, hence why we need to check whether it's greater than MAX_LOG_LINE_CHARS and if so set
+ * it to MAX_LOG_LINE_CHARS, preventing a buffer overrun further down this function.
+ */
+
+ /* check if there was a complete failure to write this part of the message, in which case return */
+ if( chars_written < 0 )
+ {
+ fprintf( stderr, "nwipe_log: snprintf error when writing log line to memory.\n" );
+ r = pthread_mutex_unlock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
+ return;
+ }
+ }
+ else
+ {
+ if( ( line_current_pos + chars_written ) > MAX_LOG_LINE_CHARS )
+ {
+ fprintf( stderr,
+ "nwipe_log: Warning! The log line has been truncated as it exceeded %i characters\n",
+ MAX_LOG_LINE_CHARS );
+ line_current_pos = MAX_LOG_LINE_CHARS;
+ }
+ else
+ {
+ line_current_pos += chars_written;
+ }
+ }
+
+ if( line_current_pos < MAX_LOG_LINE_CHARS )
+ {
+ switch( level )
+ {
+
+ case NWIPE_LOG_NONE:
+ case NWIPE_LOG_NOTIMESTAMP:
+ /* Do nothing. */
+ break;
+
+ /* NOTE! The debug labels, i.e. debug, info, notice etc should be left padded with spaces, in order
+ * to maintain column alignment. Pad a label to achieve the length of whatever the longest label happens
+ * to be. Important to know if you are thinking of adding another label.
+ */
+
+ case NWIPE_LOG_DEBUG:
+ chars_written =
+ snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " debug: " );
+ break;
+
+ case NWIPE_LOG_INFO:
+ chars_written =
+ snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " info: " );
+ break;
+
+ case NWIPE_LOG_NOTICE:
+ chars_written =
+ snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " notice: " );
+ break;
+
+ case NWIPE_LOG_WARNING:
+ chars_written =
+ snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, "warning: " );
+ break;
+
+ case NWIPE_LOG_ERROR:
+ chars_written =
+ snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " error: " );
+ break;
+
+ case NWIPE_LOG_FATAL:
+ chars_written =
+ snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " fatal: " );
+ break;
+
+ case NWIPE_LOG_SANITY:
+ /* TODO: Request that the user report the log. */
+ chars_written =
+ snprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, " sanity: " );
+ break;
+
+ default:
+ chars_written = snprintf(
+ message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos, "level %i: ", level );
+ }
+
+ /*
+ * Has the end of the buffer been reached ?
+ */
+ if( chars_written < 0 )
+ {
+ fprintf( stderr, "nwipe_log: snprintf error when writing log line to memory.\n" );
+ r = pthread_mutex_unlock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
+ return;
+ }
+ }
+ else
+ {
+ if( ( line_current_pos + chars_written ) > MAX_LOG_LINE_CHARS )
+ {
+ fprintf( stderr,
+ "nwipe_log: Warning! The log line has been truncated as it exceeded %i characters\n",
+ MAX_LOG_LINE_CHARS );
+ line_current_pos = MAX_LOG_LINE_CHARS;
+ }
+ else
+ {
+ line_current_pos += chars_written;
+ }
+ }
+ }
+
+ /* The variable argument pointer. */
+ va_list ap;
+
+ /* Fetch the argument list. */
+ va_start( ap, format );
+
+ /* Print the event. */
+ if( line_current_pos < MAX_LOG_LINE_CHARS )
+ {
+ chars_written =
+ vsnprintf( message_buffer + line_current_pos, MAX_LOG_LINE_CHARS - line_current_pos - 1, format, ap );
+
+ if( chars_written < 0 )
+ {
+ fprintf( stderr, "nwipe_log: snprintf error when writing log line to memory.\n" );
+ r = pthread_mutex_unlock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
+ va_end( ap );
+ return;
+ }
+ }
+ else
+ {
+ if( ( line_current_pos + chars_written ) > MAX_LOG_LINE_CHARS )
+ {
+ fprintf( stderr,
+ "nwipe_log: Warning! The log line has been truncated as it exceeded %i characters\n",
+ MAX_LOG_LINE_CHARS );
+ line_current_pos = MAX_LOG_LINE_CHARS;
+ }
+ else
+ {
+ line_current_pos += chars_written;
+ }
+ }
+ }
+
+ fflush( stdout );
+ /* Increase the current log element pointer - we will write here, deallocation is done in cleanup() in nwipe.c */
+ if( log_current_element == log_elements_allocated )
+ {
+ log_elements_allocated++;
+ result = realloc( log_lines, ( log_elements_allocated ) * sizeof( char* ) );
+ if( result == NULL )
+ {
+ fprintf( stderr, "nwipe_log: realloc failed when adding a log line.\n" );
+ r = pthread_mutex_unlock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
+ va_end( ap );
+ return;
+ }
+ }
+ log_lines = result;
+
+ /* Allocate memory for storing a single log message, deallocation is done in cleanup() in nwipe.c */
+ message_buffer_length = strlen( message_buffer ) * sizeof( char );
+ malloc_result = malloc( ( message_buffer_length + 1 ) * sizeof( char ) );
+ if( malloc_result == NULL )
+ {
+ fprintf( stderr, "nwipe_log: malloc failed when adding a log line.\n" );
+ r = pthread_mutex_unlock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
+ va_end( ap );
+ return;
+ }
+ }
+ log_lines[log_current_element] = malloc_result;
+ }
+
+ strcpy( log_lines[log_current_element], message_buffer );
+
+ /*
+ if( level >= NWIPE_LOG_WARNING )
+ {
+ vfprintf( stderr, format, ap );
+ }
+ */
+
+ /* Release the argument list. */
+ va_end( ap );
+
+ /*
+ if( level >= NWIPE_LOG_WARNING )
+ {
+ fprintf( stderr, "\n" );
+ }
+ */
+
+ /* The log file pointer. */
+ FILE* fp;
+
+ /* The log file descriptor. */
+ int fd;
+
+ if( nwipe_options.logfile[0] == '\0' )
+ {
+ if( nwipe_options.nogui )
+ {
+ printf( "%s\n", log_lines[log_current_element] );
+ log_elements_displayed++;
+ }
+ }
+ else
+ {
+ /* Open the log file for appending. */
+ fp = fopen( nwipe_options.logfile, "a" );
+
+ if( fp != NULL )
+ {
+
+ /* Get the file descriptor of the log file. */
+ fd = fileno( fp );
+
+ /* Block and lock. */
+ r = flock( fd, LOCK_EX );
+
+ if( r != 0 )
+ {
+ perror( "nwipe_log: flock:" );
+ fprintf( stderr, "nwipe_log: Unable to lock '%s' for logging.\n", nwipe_options.logfile );
+ r = pthread_mutex_unlock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
+
+ /* Unlock the file. */
+ r = flock( fd, LOCK_UN );
+ fclose( fp );
+ return;
+ }
+ }
+
+ fprintf( fp, "%s\n", log_lines[log_current_element] );
+
+ /* Unlock the file. */
+ r = flock( fd, LOCK_UN );
+
+ if( r != 0 )
+ {
+ perror( "nwipe_log: flock:" );
+ fprintf( stderr, "Error: Unable to unlock '%s' after logging.\n", nwipe_options.logfile );
+ }
+
+ /* Close the stream. */
+ r = fclose( fp );
+
+ if( r != 0 )
+ {
+ perror( "nwipe_log: fclose:" );
+ fprintf( stderr, "Error: Unable to close '%s' after logging.\n", nwipe_options.logfile );
+ }
+ }
+ else
+ {
+ /* Tell user we can't create/open the log and terminate nwipe */
+ fprintf(
+ stderr, "\nERROR:Unable to create/open '%s' for logging, permissions?\n\n", nwipe_options.logfile );
+ r = pthread_mutex_unlock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
+ }
+ user_abort = 1;
+ terminate_signal = 1;
+ return;
+ }
+ }
+
+ log_current_element++;
+
+ r = pthread_mutex_unlock( &mutex1 );
+ if( r != 0 )
+ {
+ fprintf( stderr, "nwipe_log: pthread_mutex_unlock failed. Code %i \n", r );
+ }
+ return;
+
+} /* nwipe_log */
+
+void nwipe_perror( int nwipe_errno, const char* f, const char* s )
+{
+ /**
+ * Wrapper for perror().
+ *
+ * We may wish to tweak or squelch this later.
+ *
+ */
+
+ nwipe_log( NWIPE_LOG_ERROR, "%s: %s: %s", f, s, strerror( nwipe_errno ) );
+
+} /* nwipe_perror */
+
+void nwipe_log_OSinfo()
+{
+ /* Read /proc/version, format and write to the log */
+
+ FILE* fp = NULL;
+ char OS_info_temp[MAX_SIZE_OS_STRING + 1];
+ char OS_info[MAX_SIZE_OS_STRING + 1];
+ int idx;
+ int idx2;
+ int idx3;
+ int idx4;
+
+ /* initialise OS_info & OS_info_temp strings */
+ idx = 0;
+ while( idx < MAX_SIZE_OS_STRING + 1 )
+ {
+ OS_info_temp[idx] = 0;
+ OS_info[idx] = 0;
+ idx++;
+ }
+
+ /* Open a pipe to /proc/version for reading */
+ fp = popen( "cat /proc/version", "r" );
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Unable to create a pipe to /proc/version" );
+ return;
+ }
+
+ /* Read the OS info */
+ if( fgets( OS_info_temp, MAX_SIZE_OS_STRING, fp ) == NULL )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "fget failed to read /proc/version" );
+ fclose( fp );
+ return;
+ }
+
+ /* Format the string for the log, place on multiple lines as necessary,
+ * column aligned, left offset with n (OS_info_Line_offset) spaces */
+ idx = 0;
+ idx2 = 0;
+ idx3 = OS_info_Line_Length;
+
+ while( OS_info_temp[idx] != 0 )
+ {
+ while( idx2 < idx3 && idx2 < MAX_SIZE_OS_STRING )
+ {
+ /* remove newlines from the source */
+ if( OS_info_temp[idx] == 0x0a )
+ {
+ idx++;
+ }
+
+ /* copy the character */
+ OS_info[idx2++] = OS_info_temp[idx++];
+ }
+ if( OS_info_temp[idx] != 0 )
+ {
+ OS_info[idx2++] = 0x0a;
+ idx4 = 0;
+
+ /* left indent with spaces */
+ while( idx4 < OS_info_Line_offset && idx2 < MAX_SIZE_OS_STRING )
+ {
+ OS_info[idx2++] = ' ';
+ idx4++;
+ }
+
+ /* calculate idx3 ready for next line */
+ idx3 += OS_info_Line_offset + OS_info_Line_Length;
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ nwipe_log( NWIPE_LOG_INFO, "%s", OS_info );
+ fclose( fp );
+ return;
+}
+
+int nwipe_log_sysinfo()
+{
+ FILE* fp;
+ char path[256];
+ int len;
+ int r; // A result buffer.
+
+ /*
+ * Remove or add keywords to be searched, depending on what information is to
+ * be logged, making sure the last entry in the array is a NULL string. To remove
+ * an entry simply comment out the keyword with //
+ */
+
+ /* The 0/1 after the keyword determines whether the data for this
+ * keyword is displayed when -q (anonymize) has been specified
+ * by the user. An quick reminder about multi dimensional arrays, the first
+ * []=the keyword (0-21) including the empty string. The second [] is the
+ * 1 or 0 value (0 or 1). The third [] is the index value into either string.
+ */
+ char dmidecode_keywords[][2][24] = {
+ { "bios-version", "1" },
+ { "bios-release-date", "1" },
+ { "system-manufacturer", "1" },
+ { "system-product-name", "1" },
+ { "system-version", "1" },
+ { "system-serial-number", "0" },
+ { "system-uuid", "0" },
+ { "baseboard-manufacturer", "1" },
+ { "baseboard-product-name", "1" },
+ { "baseboard-version", "1" },
+ { "baseboard-serial-number", "0" },
+ { "baseboard-asset-tag", "0" },
+ { "chassis-manufacturer", "1" },
+ { "chassis-type", "1" },
+ { "chassis-version", "1" },
+ { "chassis-serial-number", "0" },
+ { "chassis-asset-tag", "0" },
+ { "processor-family", "1" },
+ { "processor-manufacturer", "1" },
+ { "processor-version", "1" },
+ { "processor-frequency", "1" },
+ { "", "" } // terminates the keyword array. DO NOT REMOVE
+ };
+
+ char dmidecode_command[] = "dmidecode -s %s";
+ char dmidecode_command2[] = "/sbin/dmidecode -s %s";
+ char dmidecode_command3[] = "/usr/bin/dmidecode -s %s";
+ char* p_dmidecode_command;
+
+ char cmd[sizeof( dmidecode_keywords ) + sizeof( dmidecode_command2 )];
+
+ unsigned int keywords_idx;
+
+ keywords_idx = 0;
+
+ p_dmidecode_command = 0;
+
+ if( system( "which dmidecode > /dev/null 2>&1" ) )
+ {
+ if( system( "which /sbin/dmidecode > /dev/null 2>&1" ) )
+ {
+ if( system( "which /usr/bin/dmidecode > /dev/null 2>&1" ) )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Command not found. Install dmidecode !" );
+ }
+ else
+ {
+ p_dmidecode_command = &dmidecode_command3[0];
+ }
+ }
+ else
+ {
+ p_dmidecode_command = &dmidecode_command2[0];
+ }
+ }
+ else
+ {
+ p_dmidecode_command = &dmidecode_command[0];
+ }
+
+ if( p_dmidecode_command != 0 )
+ {
+
+ /* Run the dmidecode command to retrieve each dmidecode keyword, one at a time */
+ while( dmidecode_keywords[keywords_idx][0][0] != 0 )
+ {
+ sprintf( cmd, p_dmidecode_command, &dmidecode_keywords[keywords_idx][0][0] );
+ fp = popen( cmd, "r" );
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "nwipe_log_sysinfo: Failed to create stream to %s", cmd );
+ return 1;
+ }
+ /* Read the output a line at a time - output it. */
+ while( fgets( path, sizeof( path ) - 1, fp ) != NULL )
+ {
+ /* Remove any trailing return from the string, as nwipe_log automatically adds a return */
+ len = strlen( path );
+ if( path[len - 1] == '\n' )
+ {
+ path[len - 1] = 0;
+ }
+ if( nwipe_options.quiet )
+ {
+ if( *( &dmidecode_keywords[keywords_idx][1][0] ) == '0' )
+ {
+ nwipe_log(
+ NWIPE_LOG_NOTICE, "%s = %s", &dmidecode_keywords[keywords_idx][0][0], "XXXXXXXXXXXXXXX" );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "%s = %s", &dmidecode_keywords[keywords_idx][0][0], path );
+ }
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "%s = %s", &dmidecode_keywords[keywords_idx][0][0], path );
+ }
+ }
+ /* close */
+ r = pclose( fp );
+ if( r > 0 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING,
+ "nwipe_log_sysinfo(): dmidecode failed, \"%s\" exit status = %u",
+ cmd,
+ WEXITSTATUS( r ) );
+ return 1;
+ }
+ keywords_idx++;
+ }
+ }
+ return 0;
+}
+
+void nwipe_log_summary( nwipe_context_t** ptr, int nwipe_selected )
+{
+ /* Prints two summary tables, the first is the device pass and verification summary
+ * and the second is the main summary table detaining the drives, status, throughput,
+ * model and serial number */
+ int i;
+ int idx_src;
+ int idx_dest;
+ char device[18];
+ char status[9];
+ char throughput[13];
+ char total_throughput_string[13];
+ char summary_top_border[256];
+ char summary_top_column_titles[256];
+ char blank[3];
+ char verify[3];
+ // char duration[5];
+ char duration[314];
+ char model[18];
+ char serial_no[20];
+ char exclamation_flag[2];
+ int hours;
+ int minutes;
+ int seconds;
+ u64 total_duration_seconds;
+ u64 total_throughput;
+ nwipe_context_t** c;
+ c = ptr;
+
+ exclamation_flag[0] = 0;
+ device[0] = 0;
+ status[0] = 0;
+ throughput[0] = 0;
+ summary_top_border[0] = 0;
+ summary_top_column_titles[0] = 0;
+ blank[0] = 0;
+ verify[0] = 0;
+ duration[0] = 0;
+ model[0] = 0;
+ serial_no[0] = 0;
+ hours = 0;
+ minutes = 0;
+ seconds = 0;
+
+ /* A time buffer. */
+ time_t t;
+
+ /* A pointer to the system time struct. */
+ struct tm* p;
+
+ /* Nothing to do, user never started a wipe so no summary table required. */
+ if( global_wipe_status == 0 )
+ {
+ return;
+ }
+
+ /* Print the pass and verifications table */
+
+ /* IMPORTANT: Keep maximum columns (line length) to 80 characters for use with 80x30 terminals, Shredos, ALT-F2 etc
+ * --------------------------------01234567890123456789012345678901234567890123456789012345678901234567890123456789-*/
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP, "" );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "******************************** Error Summary *********************************" );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP, "! Device | Pass Errors | Verifications Errors | Fdatasync I\\O Errors" );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "--------------------------------------------------------------------------------" );
+
+ for( i = 0; i < nwipe_selected; i++ )
+ {
+ if( c[i]->pass_errors != 0 || c[i]->verify_errors != 0 || c[i]->fsyncdata_errors != 0 )
+ {
+ strncpy( exclamation_flag, "!", 1 );
+ exclamation_flag[1] = 0;
+ }
+ else
+ {
+ strncpy( exclamation_flag, " ", 1 );
+ exclamation_flag[1] = 0;
+ }
+
+ /* Device name, strip any prefixed /dev/.. leaving up to 6 right justified
+ * characters eg " sda", prefixed with space to 6 characters, note that
+ * we are processing the strings right to left */
+
+ idx_dest = 6;
+ device[idx_dest--] = 0;
+ idx_src = strlen( c[i]->device_name );
+ idx_src--;
+
+ nwipe_strip_path( device, c[i]->device_name );
+
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "%s %s | %10llu | %10llu | %10llu",
+ exclamation_flag,
+ device,
+ c[i]->pass_errors,
+ c[i]->verify_errors,
+ c[i]->fsyncdata_errors );
+ }
+
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "********************************************************************************" );
+
+ /* Print the main summary table */
+
+ /* initialise */
+ total_throughput = 0;
+
+ /* Get the current time. */
+ t = time( NULL );
+ p = localtime( &t );
+ /* IMPORTANT: Keep maximum columns (line length) to 80 characters for use with 80x30 terminals, Shredos, ALT-F2 etc
+ * --------------------------------01234567890123456789012345678901234567890123456789012345678901234567890123456789-*/
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP, "" );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "********************************* Drive Status *********************************" );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP, "! Device | Status | Thru-put | HH:MM:SS | Model/Serial Number" );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "--------------------------------------------------------------------------------" );
+ /* Example layout:
+ * "! sdb |--FAIL--| 120MB/s | 01:22:01 | WD6788.8488YNHj/ZX677888388-N "
+ * ); " sdc | Erased | 120MB/s | 01:25:04 | WD6784.8488JKGG/ZX677888388-N " ); " sdv | Erased |
+ * 120MB/s | 01:19:07 | WD6788.848HHDDR/ZX677888388-N " ); End of Example layout */
+
+ for( i = 0; i < nwipe_selected; i++ )
+ {
+ /* Device name, strip any prefixed /dev/.. leaving up to 8 right justified
+ * characters eg " sda", prefixed with space to 8 characters, note that
+ * we are processing the strings right to left */
+
+ nwipe_strip_path( device, c[i]->device_name );
+
+ extern int user_abort;
+
+ /* Any errors ? if so set the exclamation_flag and fail message,
+ * All status messages should be eight characters EXACTLY !
+ */
+ if( c[i]->pass_errors != 0 || c[i]->verify_errors != 0 || c[i]->fsyncdata_errors != 0 )
+ {
+ strncpy( exclamation_flag, "!", 1 );
+ exclamation_flag[1] = 0;
+
+ strncpy( status, "-FAILED-", 8 );
+ status[8] = 0;
+ }
+ else
+ {
+ if( c[i]->wipe_status == 0 )
+ {
+ strncpy( exclamation_flag, " ", 1 );
+ exclamation_flag[1] = 0;
+
+ strncpy( status, " Erased ", 8 );
+ status[8] = 0;
+ }
+ else
+ {
+ if( user_abort == 1 )
+ {
+ strncpy( exclamation_flag, "!", 1 );
+ exclamation_flag[1] = 0;
+
+ strncpy( status, "UABORTED", 8 );
+ status[8] = 0;
+ }
+ else
+ {
+ /* If this ever happens, there is a bug ! */
+ strncpy( exclamation_flag, " ", 1 );
+ exclamation_flag[1] = 0;
+
+ strncpy( status, "INSANITY", 8 );
+ status[8] = 0;
+ }
+ }
+ }
+
+ /* Determine the size of throughput so that the correct nomenclature can be used */
+ Determine_C_B_nomenclature( c[i]->throughput, throughput, 13 );
+
+ /* Add this devices throughput to the total throughput */
+ total_throughput += c[i]->throughput;
+
+ /* Retrieve the duration of the wipe in seconds and convert to hours and minutes and seconds */
+
+ if( c[i]->start_time != 0 && c[i]->end_time != 0 )
+ {
+ /* For a summary when the wipe has finished */
+ c[i]->duration = difftime( c[i]->end_time, c[i]->start_time );
+ }
+ else
+ {
+ if( c[i]->start_time != 0 && c[i]->end_time == 0 )
+ {
+ /* For a summary in the event of a system shutdown */
+ c[i]->duration = difftime( t, c[i]->start_time );
+ }
+ }
+
+ total_duration_seconds = (u64) c[i]->duration;
+
+ /* Convert binary seconds into three binary variables, hours, minutes and seconds */
+ convert_seconds_to_hours_minutes_seconds( total_duration_seconds, &hours, &minutes, &seconds );
+
+ /* Device Model */
+ strncpy( model, c[i]->device_model, 17 );
+ model[17] = 0;
+
+ /* Serial No. */
+ strncpy( serial_no, c[i]->device_serial_no, 20 );
+ model[17] = 0;
+
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "%s %s |%s| %s/s | %02i:%02i:%02i | %s/%s",
+ exclamation_flag,
+ device,
+ status,
+ throughput,
+ hours,
+ minutes,
+ seconds,
+ model,
+ serial_no );
+ }
+
+ /* Determine the size of throughput so that the correct nomenclature can be used */
+ Determine_C_B_nomenclature( total_throughput, total_throughput_string, 13 );
+
+ /* Blank abbreviations used in summary table B=blank, NB=no blank */
+ if( nwipe_options.noblank )
+ {
+ strcpy( blank, "NB" );
+ }
+ else
+ {
+ strcpy( blank, "B" );
+ }
+
+ /* Verify abbreviations used in summary table */
+ switch( nwipe_options.verify )
+ {
+ case NWIPE_VERIFY_NONE:
+ strcpy( verify, "NV" );
+ break;
+
+ case NWIPE_VERIFY_LAST:
+ strcpy( verify, "VL" );
+ break;
+
+ case NWIPE_VERIFY_ALL:
+ strcpy( verify, "VA" );
+ break;
+ }
+
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "--------------------------------------------------------------------------------" );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "[%i/%02i/%02i %02i:%02i:%02i] Total Throughput %s/s, %s, %iR+%s+%s",
+ 1900 + p->tm_year,
+ 1 + p->tm_mon,
+ p->tm_mday,
+ p->tm_hour,
+ p->tm_min,
+ p->tm_sec,
+ total_throughput_string,
+ nwipe_method_label( nwipe_options.method ),
+ nwipe_options.rounds,
+ blank,
+ verify );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP,
+ "********************************************************************************" );
+ nwipe_log( NWIPE_LOG_NOTIMESTAMP, "" );
+}
+
+void Determine_C_B_nomenclature( u64 speed, char* result, int result_array_size )
+{
+
+ /* C_B ? Determine Capacity or Bandwidth nomenclature
+ *
+ * A pointer to a result character string with a minimum of 13 characters in length
+ * should be provided.
+ *
+ * Outputs a string of the form xxxTB/s, xxxGB/s, xxxMB/s, xxxKB/s B/s depending on the value of 'speed'
+ */
+
+ /* Initialise the output array */
+ int idx = 0;
+
+ while( idx < result_array_size )
+ {
+ result[idx++] = 0;
+ }
+
+ /* Determine the size of throughput so that the correct nomenclature can be used */
+ if( speed >= INT64_C( 1000000000000 ) )
+ {
+ snprintf( result, result_array_size, "%3llu TB", speed / INT64_C( 1000000000000 ) );
+ }
+ else if( speed >= INT64_C( 1000000000 ) )
+ {
+ snprintf( result, result_array_size, "%3llu GB", speed / INT64_C( 1000000000 ) );
+ }
+ else if( speed >= INT64_C( 1000000 ) )
+ {
+ snprintf( result, result_array_size, "%3llu MB", speed / INT64_C( 1000000 ) );
+ }
+ else if( speed >= INT64_C( 1000 ) )
+ {
+ snprintf( result, result_array_size, "%3llu KB", speed / INT64_C( 1000 ) );
+ }
+ else
+ {
+ snprintf( result, result_array_size, "%3llu B", speed / INT64_C( 1 ) );
+ }
+}
+
+void convert_seconds_to_hours_minutes_seconds( u64 total_seconds, int* hours, int* minutes, int* seconds )
+{
+ /* Convert binary seconds into binary hours, minutes and seconds */
+
+ if( total_seconds % 60 )
+ {
+ *minutes = total_seconds / 60;
+
+ *seconds = total_seconds - ( *minutes * 60 );
+ }
+ else
+ {
+ *minutes = total_seconds / 60;
+
+ *seconds = 0;
+ }
+ if( *minutes > 59 )
+ {
+ *hours = *minutes / 60;
+ if( *minutes % 60 )
+ {
+ *minutes = *minutes - ( *hours * 60 );
+ }
+ else
+ {
+ *minutes = 0;
+ }
+ }
+}
+
+int nwipe_strip_path( char* output, char* input )
+{
+ /* Take the input string, say "/dev/sda" and remove the "/dev/", prefix the result
+ * with 'length' spaces. So if length=8 and input=/dev/sda, output will
+ * be " sda", a string 8 characters long right justified with spaces.
+ */
+ int idx_dest;
+ int idx_src;
+ idx_dest = 8;
+ // idx_dest = length;
+ output[idx_dest--] = 0;
+ idx_src = strlen( input );
+ idx_src--;
+
+ while( idx_dest >= 0 )
+ {
+ /* if the device name contains a / start prefixing spaces */
+ if( input[idx_src] == '/' )
+ {
+ output[idx_dest--] = ' ';
+ continue;
+ }
+ if( idx_src >= 0 )
+ {
+ output[idx_dest--] = input[idx_src--];
+ }
+ }
+}
diff --git a/src/logging.h b/src/logging.h
new file mode 100644
index 0000000..b2d4b75
--- /dev/null
+++ b/src/logging.h
@@ -0,0 +1,52 @@
+/*
+ * logging.c: Logging facilities for nwipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.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.
+ *
+ */
+
+#ifndef LOGGING_H_
+#define LOGGING_H_
+
+/* Maximum size of a log message */
+#define MAX_LOG_LINE_CHARS 512
+
+#define MAX_SIZE_OS_STRING 512 /* Maximum size of acceptable OS string */
+#define OS_info_Line_offset 31 /* OS_info line offset in log */
+#define OS_info_Line_Length 48 /* OS_info line length */
+
+typedef enum nwipe_log_t_ {
+ NWIPE_LOG_NONE = 0,
+ NWIPE_LOG_DEBUG, // TODO: Very verbose logging.
+ NWIPE_LOG_INFO, // TODO: Verbose logging.
+ NWIPE_LOG_NOTICE, // Most logging happens at this level.
+ NWIPE_LOG_WARNING, // Things that the user should know about.
+ NWIPE_LOG_ERROR, // Non-fatal errors that result in failure.
+ NWIPE_LOG_FATAL, // Errors that cause the program to exit.
+ NWIPE_LOG_SANITY, // Programming errors.
+ NWIPE_LOG_NOTIMESTAMP // logs the message without the timestamp
+} nwipe_log_t;
+
+void nwipe_log( nwipe_log_t level, const char* format, ... );
+void nwipe_perror( int nwipe_errno, const char* f, const char* s );
+void nwipe_log_OSinfo();
+int nwipe_log_sysinfo();
+void nwipe_log_summary( nwipe_context_t**, int ); // This produces the wipe status table on exit
+void Determine_C_B_nomenclature( u64, char*, int );
+void convert_seconds_to_hours_minutes_seconds( u64, int*, int*, int* );
+int nwipe_strip_path( char*, char* );
+
+#endif /* LOGGING_H_ */
diff --git a/src/method.c b/src/method.c
new file mode 100644
index 0000000..5f5f298
--- /dev/null
+++ b/src/method.c
@@ -0,0 +1,1375 @@
+/*
+ * method.c: Method implementations 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.
+ */
+
+/* HOWTO: Add another wipe method.
+ *
+ * 1. Create a new function here and add the prototype to the 'method.h' file.
+ * 2. Update nwipe_method_label() appropriately.
+ * 3. Put the passes that you wish to run into a nwipe_pattern_t array.
+ * 4. Call nwipe_runmethod() with your array of patterns.
+ * 5. Copy-and-paste within the 'options.c' file so that the new method can be invoked.
+ * 6. Optionally try to plug your function into 'gui.c'.
+ * 7. Update the function 'calculate_round_size()' with the new method.
+ *
+ *
+ * WARNING: Remember to pad all pattern arrays with { 0, NULL }.
+ *
+ * WARNING: Never change nwipe_options after calling a method.
+ *
+ * NOTE: The nwipe_runmethod function appends a user selectable final blanking (zero) pass to all methods.
+ *
+ */
+
+#include <stdint.h>
+
+#include "nwipe.h"
+#include "context.h"
+#include "method.h"
+#include "prng.h"
+#include "options.h"
+#include "pass.h"
+#include "logging.h"
+
+/*
+ * Comment Legend
+ *
+ * "method" An ordered set of patterns.
+ * "pattern" The magic bits that will be written to a device.
+ * "pass" Reading or writing one pattern to an entire device.
+ * "rounds" The number of times that a method will be applied to a device.
+ *
+ */
+
+const char* nwipe_dod522022m_label = "DoD 5220.22-M";
+const char* nwipe_dodshort_label = "DoD Short";
+const char* nwipe_gutmann_label = "Gutmann Wipe";
+const char* nwipe_ops2_label = "RCMP TSSIT OPS-II";
+const char* nwipe_random_label = "PRNG Stream";
+const char* nwipe_zero_label = "Fill With Zeros";
+const char* nwipe_one_label = "Fill With Ones";
+const char* nwipe_verify_zero_label = "Verify Zeros (0x00)";
+const char* nwipe_verify_one_label = "Verify Ones (0xFF)";
+const char* nwipe_is5enh_label = "HMG IS5 Enhanced";
+
+const char* nwipe_unknown_label = "Unknown Method (FIXME)";
+
+const char* nwipe_method_label( void* method )
+{
+ /**
+ * Returns a pointer to the name of the method function.
+ *
+ */
+
+ if( method == &nwipe_dod522022m )
+ {
+ return nwipe_dod522022m_label;
+ }
+ if( method == &nwipe_dodshort )
+ {
+ return nwipe_dodshort_label;
+ }
+ if( method == &nwipe_gutmann )
+ {
+ return nwipe_gutmann_label;
+ }
+ if( method == &nwipe_ops2 )
+ {
+ return nwipe_ops2_label;
+ }
+ if( method == &nwipe_random )
+ {
+ return nwipe_random_label;
+ }
+ if( method == &nwipe_zero )
+ {
+ return nwipe_zero_label;
+ }
+ if( method == &nwipe_one )
+ {
+ return nwipe_one_label;
+ }
+ if( method == &nwipe_verify_zero )
+ {
+ return nwipe_verify_zero_label;
+ }
+ if( method == &nwipe_verify_one )
+ {
+ return nwipe_verify_one_label;
+ }
+ if( method == &nwipe_is5enh )
+ {
+ return nwipe_is5enh_label;
+ }
+
+ /* else */
+ return nwipe_unknown_label;
+
+} /* nwipe_method_label */
+
+void* nwipe_zero( void* ptr )
+{
+ /**
+ * Fill the device with zeroes.
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* setup for a zero-fill. */
+
+ char zerofill[1] = { '\x00' };
+ nwipe_pattern_t patterns[] = { { 1, &zerofill[0] }, // pass 1: 0s
+ { 0, NULL } };
+
+ /* Run the method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_zero */
+
+void* nwipe_one( void* ptr )
+{
+ /**
+ * Fill the device with ones.
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* setup for a zero-fill. */
+
+ char onefill[1] = { '\xFF' };
+ nwipe_pattern_t patterns[] = { { 1, &onefill[0] }, // pass 1: 1s
+ { 0, NULL } };
+
+ /* Run the method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_one */
+
+void* nwipe_verify_zero( void* ptr )
+{
+ /**
+ * Verify the device is full of zeros.
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* Do nothing because nwipe_runmethod appends a zero-fill. */
+ nwipe_pattern_t patterns[] = { { 0, NULL } };
+
+ /* Run the method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_verify zeros */
+
+void* nwipe_verify_one( void* ptr )
+{
+ /**
+ * Verify the device is full of ones.
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* Do nothing because nwipe_runmethod appends a zero-fill. */
+ nwipe_pattern_t patterns[] = { { 0, NULL } };
+
+ /* Run the method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_verify */
+
+void* nwipe_dod522022m( void* ptr )
+{
+ /**
+ * United States Department of Defense 5220.22-M standard wipe.
+ *
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* A result holder. */
+ int r;
+
+ /* Random characters. (Elements 2 and 6 are unused.) */
+ char dod[7];
+
+ nwipe_pattern_t patterns[] = { { 1, &dod[0] }, // Pass 1: A random character.
+ { 1, &dod[1] }, // Pass 2: The bitwise complement of pass 1.
+ { -1, "" }, // Pass 3: A random stream.
+ { 1, &dod[3] }, // Pass 4: A random character.
+ { 1, &dod[4] }, // Pass 5: A random character.
+ { 1, &dod[5] }, // Pass 6: The bitwise complement of pass 5.
+ { -1, "" }, // Pass 7: A random stream.
+ { 0, NULL } };
+
+ /* Load the array with random characters. */
+ r = read( c->entropy_fd, &dod, sizeof( dod ) );
+
+ /* NOTE: Only the random data in dod[0], dod[3], and dod[4] is actually used. */
+
+ /* Check the result. */
+ if( r != sizeof( dod ) )
+ {
+ r = errno;
+ nwipe_perror( r, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the %s method.", nwipe_dod522022m_label );
+
+ /* Ensure a negative return. */
+ if( r < 0 )
+ {
+ c->result = r;
+ return NULL;
+ }
+ else
+ {
+ c->result = -1;
+ return NULL;
+ }
+ }
+
+ /* Pass 2 is the bitwise complement of Pass 1. */
+ dod[1] = ~dod[0];
+
+ /* Pass 4 is the bitwise complement of Pass 3. */
+ dod[5] = ~dod[4];
+
+ /* Run the DoD 5220.22-M method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_dod522022m */
+
+void* nwipe_dodshort( void* ptr )
+{
+ /**
+ * United States Department of Defense 5220.22-M short wipe.
+ * This method is comprised of passes 1,2,7 from the standard wipe.
+ *
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* A result holder. */
+ int r;
+
+ /* Random characters. (Element 3 is unused.) */
+ char dod[3];
+
+ nwipe_pattern_t patterns[] = { { 1, &dod[0] }, // Pass 1: A random character.
+ { 1, &dod[1] }, // Pass 2: The bitwise complement of pass 1.
+ { -1, "" }, // Pass 3: A random stream.
+ { 0, NULL } };
+
+ /* Load the array with random characters. */
+ r = read( c->entropy_fd, &dod, sizeof( dod ) );
+
+ /* NOTE: Only the random data in dod[0] is actually used. */
+
+ /* Check the result. */
+ if( r != sizeof( dod ) )
+ {
+ r = errno;
+ nwipe_perror( r, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the %s method.", nwipe_dodshort_label );
+
+ /* Ensure a negative return. */
+ if( r < 0 )
+ {
+ c->result = r;
+ return NULL;
+ }
+ else
+ {
+ c->result = -1;
+ return NULL;
+ }
+ }
+
+ /* Pass 2 is the bitwise complement of Pass 1. */
+ dod[1] = ~dod[0];
+
+ /* Run the DoD 5220.022-M short method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_dodshort */
+
+void* nwipe_gutmann( void* ptr )
+{
+ /**
+ * Peter Gutmann's wipe.
+ *
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* Define the Gutmann method. */
+ nwipe_pattern_t book[] = { { -1, "" }, // Random pass.
+ { -1, "" }, // Random pass.
+ { -1, "" }, // Random pass.
+ { -1, "" }, // Random pass.
+ { 3, "\x55\x55\x55" }, // Static pass: 0x555555 01010101 01010101 01010101
+ { 3, "\xAA\xAA\xAA" }, // Static pass: 0XAAAAAA 10101010 10101010 10101010
+ { 3, "\x92\x49\x24" }, // Static pass: 0x924924 10010010 01001001 00100100
+ { 3, "\x49\x24\x92" }, // Static pass: 0x492492 01001001 00100100 10010010
+ { 3, "\x24\x92\x49" }, // Static pass: 0x249249 00100100 10010010 01001001
+ { 3, "\x00\x00\x00" }, // Static pass: 0x000000 00000000 00000000 00000000
+ { 3, "\x11\x11\x11" }, // Static pass: 0x111111 00010001 00010001 00010001
+ { 3, "\x22\x22\x22" }, // Static pass: 0x222222 00100010 00100010 00100010
+ { 3, "\x33\x33\x33" }, // Static pass: 0x333333 00110011 00110011 00110011
+ { 3, "\x44\x44\x44" }, // Static pass: 0x444444 01000100 01000100 01000100
+ { 3, "\x55\x55\x55" }, // Static pass: 0x555555 01010101 01010101 01010101
+ { 3, "\x66\x66\x66" }, // Static pass: 0x666666 01100110 01100110 01100110
+ { 3, "\x77\x77\x77" }, // Static pass: 0x777777 01110111 01110111 01110111
+ { 3, "\x88\x88\x88" }, // Static pass: 0x888888 10001000 10001000 10001000
+ { 3, "\x99\x99\x99" }, // Static pass: 0x999999 10011001 10011001 10011001
+ { 3, "\xAA\xAA\xAA" }, // Static pass: 0xAAAAAA 10101010 10101010 10101010
+ { 3, "\xBB\xBB\xBB" }, // Static pass: 0xBBBBBB 10111011 10111011 10111011
+ { 3, "\xCC\xCC\xCC" }, // Static pass: 0xCCCCCC 11001100 11001100 11001100
+ { 3, "\xDD\xDD\xDD" }, // Static pass: 0xDDDDDD 11011101 11011101 11011101
+ { 3, "\xEE\xEE\xEE" }, // Static pass: 0xEEEEEE 11101110 11101110 11101110
+ { 3, "\xFF\xFF\xFF" }, // Static pass: 0xFFFFFF 11111111 11111111 11111111
+ { 3, "\x92\x49\x24" }, // Static pass: 0x924924 10010010 01001001 00100100
+ { 3, "\x49\x24\x92" }, // Static pass: 0x492492 01001001 00100100 10010010
+ { 3, "\x24\x92\x49" }, // Static pass: 0x249249 00100100 10010010 01001001
+ { 3, "\x6D\xB6\xDB" }, // Static pass: 0x6DB6DB 01101101 10110110 11011011
+ { 3, "\xB6\xDB\x6D" }, // Static pass: 0xB6DB6D 10110110 11011011 01101101
+ { 3, "\xDB\x6D\xB6" }, // Static pass: 0XDB6DB6 11011011 01101101 10110110
+ { -1, "" }, // Random pass.
+ { -1, "" }, // Random pass.
+ { -1, "" }, // Random pass.
+ { -1, "" }, // Random pass.
+ { 0, NULL } };
+
+ /* Put the book array into this array in random order. */
+ nwipe_pattern_t patterns[36];
+
+ /* An entropy buffer. */
+ u16 s[27];
+
+ /* Load the array with random characters. */
+ ssize_t r = read( c->entropy_fd, &s, sizeof( s ) );
+ if( r != sizeof( s ) )
+ {
+ r = errno;
+ nwipe_perror( r, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the %s method.", nwipe_gutmann_label );
+
+ /* Ensure a negative return. */
+ if( r < 0 )
+ {
+ c->result = r;
+ return NULL;
+ }
+ else
+ {
+ c->result = -1;
+ return NULL;
+ }
+ }
+
+ // First 4 random passes
+ for( int i = 0; i <= 3; ++i )
+ {
+ patterns[i] = book[i];
+ }
+ // Middle 27 passes in random order
+ for( int i = 26; i >= 0; --i )
+ {
+ /* Get a random integer that is less than the first index 'i'. */
+ int n = (int) ( (double) ( s[i] ) / (double) ( 0x0000FFFF + 1 ) * (double) ( i + 1 ) );
+
+ /* Initialize the secondary index. */
+ int j = 3;
+
+ while( n-- >= 0 )
+ {
+ /* Advance 'j' by 'n' positions... */
+ j += 1;
+
+ /* ... but don't count 'book' elements that have already been copied. */
+ while( book[j].length == 0 )
+ {
+ j += 1;
+ }
+ }
+
+ /* Copy the element. */
+ patterns[i + 4] = book[j];
+
+ /* Mark this element as having been used. */
+ book[j].length = 0;
+ }
+ // Last 4 random passes
+ for( int i = 31; i <= 34; ++i )
+ {
+ patterns[i] = book[i];
+ }
+
+ /* Ensure that the array is terminated. */
+ patterns[35].length = 0;
+ patterns[35].s = NULL;
+
+ /* Run the Gutmann method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_gutmann */
+
+void* nwipe_ops2( void* ptr )
+{
+ /**
+ * Royal Canadian Mounted Police
+ * Technical Security Standard for Information Technology
+ * Appendix OPS-II: Media Sanitization
+ *
+ * NOTE: The last pass of this method is specially handled by nwipe_runmethod.
+ *
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* A generic array index. */
+ int i;
+
+ /* A generic result buffer. */
+ int r;
+
+ /* A buffer for random characters. */
+ char* s;
+
+ /* A buffer for the bitwise complements of 's'. */
+ char* t;
+
+ /* The element count of 's' and 't'. */
+ u32 u;
+
+ /* The pattern array for this method is dynamically allocated. */
+ nwipe_pattern_t* patterns;
+
+ /* The element count of 'patterns'. */
+ u32 q;
+
+ /* We need one random character per round. */
+ u = 1 * nwipe_options.rounds;
+
+ /* Allocate the array of random characters. */
+ s = malloc( sizeof( char ) * u );
+
+ if( s == NULL )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate the random character array." );
+ c->result = -1;
+ return NULL;
+ }
+
+ /* Allocate the array of complement characters. */
+ t = malloc( sizeof( char ) * u );
+
+ if( t == NULL )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate the complement character array." );
+ c->result = -1;
+ free( s );
+ return NULL;
+ }
+
+ /* We need eight pattern elements per round, plus one for padding. */
+ q = 8 * u + 1;
+
+ /* Allocate the pattern array. */
+ patterns = malloc( sizeof( nwipe_pattern_t ) * q );
+
+ if( patterns == NULL )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate the pattern array." );
+ c->result = -1;
+ free( s );
+ free( t );
+ return NULL;
+ }
+
+ /* Load the array of random characters. */
+ r = read( c->entropy_fd, s, u );
+
+ if( r != u )
+ {
+ r = errno;
+ nwipe_perror( r, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the %s method.", nwipe_ops2_label );
+
+ if( r < 0 )
+ {
+ c->result = r;
+ free( s );
+ free( t );
+ free( patterns );
+ return NULL;
+ }
+ else
+ {
+ c->result = -1;
+ free( s );
+ free( t );
+ free( patterns );
+ return NULL;
+ }
+ }
+
+ for( i = 0; i < u; i += 1 )
+ {
+ /* Populate the array of complements. */
+ t[i] = ~s[i];
+ }
+
+ for( i = 0; i < u; i += 8 )
+ {
+ /* Populate the array of patterns. */
+
+ /* Even elements point to the random characters. */
+ patterns[i * 4 + 0].length = 1;
+ patterns[i * 4 + 0].s = &s[i];
+ patterns[i * 4 + 2].length = 1;
+ patterns[i * 4 + 2].s = &s[i];
+ patterns[i * 4 + 4].length = 1;
+ patterns[i * 4 + 4].s = &s[i];
+ patterns[i * 4 + 6].length = 1;
+ patterns[i * 4 + 6].s = &s[i];
+
+ /* Odd elements point to the complement characters. */
+ patterns[i * 4 + 1].length = 1;
+ patterns[i * 4 + 1].s = &t[i];
+ patterns[i * 4 + 3].length = 1;
+ patterns[i * 4 + 3].s = &t[i];
+ patterns[i * 4 + 5].length = 1;
+ patterns[i * 4 + 5].s = &t[i];
+ patterns[i * 4 + 7].length = 1;
+ patterns[i * 4 + 7].s = &t[i];
+ }
+
+ /* Ensure that the array is terminated. */
+ patterns[q - 1].length = 0;
+ patterns[q - 1].s = NULL;
+
+ /* Run the TSSIT OPS-II method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Release the random character buffer. */
+ free( s );
+
+ /* Release the complement character buffer */
+ free( t );
+
+ /* Release the pattern buffer. */
+ free( patterns );
+
+ /* We're done. */
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_ops2 */
+
+void* nwipe_is5enh( void* ptr )
+{
+ nwipe_context_t* c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ c->wipe_status = 1;
+
+ char is5enh[3] = { '\x00', '\xFF', '\x00' };
+ nwipe_pattern_t patterns[] = { { 1, &is5enh[0] }, // Pass 1: 0s
+ { 1, &is5enh[1] }, // Pass 2: 1s
+ { -1, &is5enh[2] }, // Pass 3: random bytes with verification
+ { 0, NULL } };
+ c->result = nwipe_runmethod( c, patterns );
+
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_is5enh */
+
+void* nwipe_random( void* ptr )
+{
+ /**
+ * Fill the device with a stream from the PRNG.
+ *
+ */
+
+ nwipe_context_t* c;
+ c = (nwipe_context_t*) ptr;
+
+ /* get current time at the start of the wipe */
+ time( &c->start_time );
+
+ /* set wipe in progress flag for GUI */
+ c->wipe_status = 1;
+
+ /* Define the random method. */
+ nwipe_pattern_t patterns[] = { { -1, "" }, { 0, NULL } };
+
+ /* Run the method. */
+ c->result = nwipe_runmethod( c, patterns );
+
+ /* Finished. Set the wipe_status flag so that the GUI knows */
+ c->wipe_status = 0;
+
+ /* get current time at the end of the wipe */
+ time( &c->end_time );
+
+ return NULL;
+} /* nwipe_random */
+
+int nwipe_runmethod( nwipe_context_t* c, nwipe_pattern_t* patterns )
+{
+ /**
+ * Writes patterns to the device.
+ *
+ */
+
+ /* The result holder. */
+ int r;
+
+ /* An index variable. */
+ int i = 0;
+
+ /* Variable to track if it is the last pass */
+ int lastpass = 0;
+
+ i = 0;
+
+ /* The zero-fill pattern for the final pass of most methods. */
+ nwipe_pattern_t pattern_zero = { 1, "\x00" };
+
+ /* The one-fill pattern for verification of the ones fill */
+ nwipe_pattern_t pattern_one = { 1, "\xFF" };
+
+ /* Create the PRNG state buffer. */
+ c->prng_seed.length = NWIPE_KNOB_PRNG_STATE_LENGTH;
+ c->prng_seed.s = malloc( c->prng_seed.length );
+
+ /* Check the memory allocation. */
+ if( !c->prng_seed.s )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the prng seed buffer." );
+ return -1;
+ }
+
+ /* Count the number of patterns in the array. */
+ while( patterns[i].length )
+ {
+ i += 1;
+ }
+
+ /* Tell the parent the number of device passes that will be run in one round. */
+ c->pass_count = i;
+
+ /* Set the number of bytes that will be written across all passes in one round. */
+ c->pass_size = c->pass_count * c->device_size;
+
+ /* For the selected method, calculate the correct round_size value (for correct percentage calculation) */
+ calculate_round_size( c );
+
+ /* If only verifying then the round size is the device size */
+ if( nwipe_options.method == &nwipe_verify_zero || nwipe_options.method == &nwipe_verify_one )
+ {
+ c->round_size = c->device_size;
+ }
+
+ /* Initialize the working round counter. */
+ c->round_working = 0;
+
+ nwipe_log(
+ NWIPE_LOG_NOTICE, "Invoking method '%s' on %s", nwipe_method_label( nwipe_options.method ), c->device_name );
+
+ while( c->round_working < c->round_count )
+ {
+ /* Increment the round counter. */
+ c->round_working += 1;
+
+ nwipe_log(
+ NWIPE_LOG_NOTICE, "Starting round %i of %i on %s", c->round_working, c->round_count, c->device_name );
+
+ /* Initialize the working pass counter. */
+ c->pass_working = 0;
+
+ for( i = 0; i < c->pass_count; i++ )
+ {
+ /* Increment the working pass. */
+ c->pass_working += 1;
+
+ /* Check if this is the last pass. */
+ if( nwipe_options.verify == NWIPE_VERIFY_LAST && nwipe_options.method != &nwipe_ops2 )
+ {
+ if( nwipe_options.noblank == 1 && c->round_working == c->round_count
+ && c->pass_working == c->pass_count )
+ {
+ lastpass = 1;
+ }
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "Starting pass %i/%i, round %i/%i, on %s",
+ c->pass_working,
+ c->pass_count,
+ c->round_working,
+ c->round_count,
+ c->device_name );
+
+ if( patterns[i].length == 0 )
+ {
+ /* Caught insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "nwipe_runmethod: A non-terminating pattern element has zero length." );
+ return -1;
+ }
+
+ if( patterns[i].length > 0 )
+ {
+
+ /* Write a static pass. */
+ c->pass_type = NWIPE_PASS_WRITE;
+ r = nwipe_static_pass( c, &patterns[i] );
+ c->pass_type = NWIPE_PASS_NONE;
+
+ /* Log number of bytes written to disk */
+ nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes written to %s", c->pass_done, c->device_name );
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+
+ if( nwipe_options.verify == NWIPE_VERIFY_ALL || lastpass == 1 )
+ {
+
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "Verifying pass %i of %i, round %i of %i, on %s",
+ c->pass_working,
+ c->pass_count,
+ c->round_working,
+ c->round_count,
+ c->device_name );
+
+ /* Verify this pass. */
+ c->pass_type = NWIPE_PASS_VERIFY;
+ r = nwipe_static_verify( c, &patterns[i] );
+ c->pass_type = NWIPE_PASS_NONE;
+
+ nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes read from %s", c->pass_done, c->device_name );
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "Verified pass %i of %i, round %i of %i, on '%s'.",
+ c->pass_working,
+ c->pass_count,
+ c->round_working,
+ c->round_count,
+ c->device_name );
+ }
+
+ } /* static pass */
+
+ else
+ {
+ c->pass_type = NWIPE_PASS_WRITE;
+
+ /* Seed the PRNG. */
+ r = read( c->entropy_fd, c->prng_seed.s, c->prng_seed.length );
+
+ /* Check the result. */
+ if( r < 0 )
+ {
+ c->pass_type = NWIPE_PASS_NONE;
+ nwipe_perror( errno, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the PRNG." );
+ return -1;
+ }
+
+ /* Check for a partial read. */
+ if( r != c->prng_seed.length )
+ {
+ /* TODO: Handle partial reads. */
+ nwipe_log( NWIPE_LOG_FATAL, "Insufficient entropy is available." );
+ return -1;
+ }
+
+ /* Write the random pass. */
+ r = nwipe_random_pass( c );
+ c->pass_type = NWIPE_PASS_NONE;
+
+ /* Log number of bytes written to disk */
+ nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes written to %s", c->pass_done, c->device_name );
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+
+ /* Make sure IS5 enhanced always verifies its PRNG pass regardless */
+ /* of the current combination of the --noblank (which influences */
+ /* the lastpass variable) and --verify options. */
+ if( nwipe_options.verify == NWIPE_VERIFY_ALL || lastpass == 1 || nwipe_options.method == &nwipe_is5enh )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "Verifying pass %i of %i, round %i of %i, on %s",
+ c->pass_working,
+ c->pass_count,
+ c->round_working,
+ c->round_count,
+ c->device_name );
+
+ /* Verify this pass. */
+ c->pass_type = NWIPE_PASS_VERIFY;
+ r = nwipe_random_verify( c );
+ c->pass_type = NWIPE_PASS_NONE;
+
+ nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes read from %s", c->pass_done, c->device_name );
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "Verified pass %i of %i, round %i of %i, on '%s'.",
+ c->pass_working,
+ c->pass_count,
+ c->round_working,
+ c->round_count,
+ c->device_name );
+ }
+
+ } /* random pass */
+
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "Finished pass %i/%i, round %i/%i, on %s",
+ c->pass_working,
+ c->pass_count,
+ c->round_working,
+ c->round_count,
+ c->device_name );
+
+ } /* for passes */
+
+ if( c->round_working < c->round_count )
+ {
+ nwipe_log(
+ NWIPE_LOG_NOTICE, "Finished round %i of %i on %s", c->round_working, c->round_count, c->device_name );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "Finished final round %i of %i on %s",
+ c->round_working,
+ c->round_count,
+ c->device_name );
+ }
+
+ } /* while rounds */
+
+ if( nwipe_options.method == &nwipe_ops2 )
+ {
+ /* NOTE: The OPS-II method specifically requires that a random pattern be left on the device. */
+
+ /* Tell the parent that we are running the final pass. */
+ c->pass_type = NWIPE_PASS_FINAL_OPS2;
+
+ /* Seed the PRNG. */
+ r = read( c->entropy_fd, c->prng_seed.s, c->prng_seed.length );
+
+ /* Check the result. */
+ if( r < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the PRNG." );
+ return -1;
+ }
+
+ /* Check for a partial read. */
+ if( r != c->prng_seed.length )
+ {
+ /* TODO: Handle partial reads. */
+ nwipe_log( NWIPE_LOG_FATAL, "Insufficient entropy is available." );
+ return -1;
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE, "Writing final random pattern to '%s'.", c->device_name );
+
+ /* The final ops2 pass. */
+ r = nwipe_random_pass( c );
+
+ nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes written to %s", c->pass_done, c->device_name );
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+
+ if( nwipe_options.verify == NWIPE_VERIFY_LAST || nwipe_options.verify == NWIPE_VERIFY_ALL )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "Verifying final random pattern FRP on %s", c->device_name );
+
+ /* Verify the final zero pass. */
+ r = nwipe_random_verify( c );
+
+ nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes read from %s", c->pass_done, c->device_name );
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Verified FRP on '%s' matches", c->device_name );
+ }
+
+ } /* final ops2 */
+
+ else if( nwipe_options.method == &nwipe_verify_zero )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "Verifying that %s is zeroed", c->device_name );
+
+ /* Verify the final zero pass. */
+ c->pass_type = NWIPE_PASS_VERIFY;
+ r = nwipe_static_verify( c, &pattern_zero );
+ c->pass_type = NWIPE_PASS_NONE;
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+ if( c->verify_errors == 0 )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Verified that %s is Zeroed.", c->device_name );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "[FAILURE] %s has not been Zeroed .", c->device_name );
+ }
+
+ } /* verify */
+
+ else if( nwipe_options.method == &nwipe_verify_one )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "Verifying that %s is Ones (0xFF)", c->device_name );
+
+ /* Verify the final ones pass. */
+ c->pass_type = NWIPE_PASS_VERIFY;
+ r = nwipe_static_verify( c, &pattern_one );
+ c->pass_type = NWIPE_PASS_NONE;
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+ if( c->verify_errors == 0 )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Verified that %s is full of ones (0xFF).", c->device_name );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "[FAILURE] %s is not full of ones (0xFF).", c->device_name );
+ }
+
+ } /* verify */
+
+ else if( nwipe_options.noblank == 0 )
+ {
+ /* Tell the user that we are on the final pass. */
+ c->pass_type = NWIPE_PASS_FINAL_BLANK;
+
+ nwipe_log( NWIPE_LOG_NOTICE, "Blanking device %s", c->device_name );
+
+ /* The final zero pass. */
+ r = nwipe_static_pass( c, &pattern_zero );
+
+ /* Log number of bytes written to disk */
+ nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes written to %s", c->pass_done, c->device_name );
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+
+ if( nwipe_options.verify == NWIPE_VERIFY_LAST || nwipe_options.verify == NWIPE_VERIFY_ALL )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "Verifying that %s is empty.", c->device_name );
+
+ /* Verify the final zero pass. */
+ c->pass_type = NWIPE_PASS_VERIFY;
+ r = nwipe_static_verify( c, &pattern_zero );
+ c->pass_type = NWIPE_PASS_NONE;
+
+ /* Log number of bytes read from disk */
+ nwipe_log( NWIPE_LOG_NOTICE, "%llu bytes read from %s", c->pass_done, c->device_name );
+
+ /* Check for a fatal error. */
+ if( r < 0 )
+ {
+ return r;
+ }
+
+ if( c->verify_errors == 0 )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Verified that %s is empty.", c->device_name );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "[FAILURE] %s Verification errors, not empty", c->device_name );
+ }
+ }
+
+ if( c->verify_errors == 0 && c->pass_errors == 0 )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Blanked device %s", c->device_name );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "[FAILURE] %s may not be blanked", c->device_name );
+ }
+
+ } /* final blank */
+
+ /* Release the state buffer. */
+ c->prng_seed.length = 0;
+ free( c->prng_seed.s );
+
+ /* Tell the parent that we have fininshed the final pass. */
+ c->pass_type = NWIPE_PASS_NONE;
+
+ if( c->verify_errors > 0 )
+ {
+ /* We finished, but with non-fatal verification errors. */
+ nwipe_log( NWIPE_LOG_ERROR, "%llu verification errors on '%s'.", c->verify_errors, c->device_name );
+ }
+
+ if( c->pass_errors > 0 )
+ {
+ /* We finished, but with non-fatal wipe errors. */
+ nwipe_log( NWIPE_LOG_ERROR, "%llu wipe errors on '%s'.", c->pass_errors, c->device_name );
+ }
+
+ /* FIXME: The 'round_errors' context member is not being used. */
+
+ if( c->pass_errors > 0 || c->round_errors > 0 || c->verify_errors > 0 )
+ {
+ /* We finished, but with non-fatal errors. */
+ return 1;
+ }
+
+ /* We finished successfully. */
+ return 0;
+
+} /* nwipe_runmethod */
+
+void calculate_round_size( nwipe_context_t* c )
+{
+ /* This is where the round size is calculated. round_size is used in the running percentage completion
+ * calculation. round size is calculated based on pass_size, pass_count, number of rounds, blanking
+ * on/off and verification All/Last/None
+ *
+ * To hopefully make this calculation more understandable, I have separated the calculations that apply to
+ * all methods and processed first then created a switch statement that contains method specific changes if any
+ */
+
+ /* Don't change the order of these values as the case statements use their index in the array */
+ void* array_methods[] = { &nwipe_zero,
+ &nwipe_ops2,
+ &nwipe_dodshort,
+ &nwipe_dod522022m,
+ &nwipe_gutmann,
+ &nwipe_random,
+ &nwipe_is5enh,
+ NULL };
+ int i;
+
+ /* This while loop allows us to effectively create a const so we can use a case statement rather than if statements.
+ * This is probably more readable as more methods may get added in the future. The code could be condensed as some
+ * methods have identical adjustments, however as there are only a few methods I felt it was easier to understand as
+ * it is, however this could be changed if necessary.
+ */
+
+ int selected_method;
+ i = 0;
+ while( array_methods[i] != NULL )
+ {
+ if( nwipe_options.method == array_methods[i] )
+ {
+ selected_method = i;
+ }
+ i++;
+ }
+
+ if( nwipe_options.verify == NWIPE_VERIFY_ALL )
+ {
+ /* We must read back all passes, so double the byte count. */
+ c->pass_size *= 2;
+ }
+
+ /* Tell the parent the number of rounds that will be run. */
+ c->round_count = nwipe_options.rounds;
+
+ /* Set the initial number of bytes that will be written across all rounds.
+ c->pass_size includes write AND verification passes if 'verify_all' is selected
+ but does not include the final blanking pass or the verify_last option */
+ c->round_size = c->pass_size;
+
+ /* Multiple the round_size by the number of rounds (times) the user wants to wipe the drive with this method. */
+ c->round_size *= c->round_count;
+
+ /* Now increase size based on whether blanking is enabled and verification */
+ if( nwipe_options.noblank == 0 )
+ {
+ /* Blanking enabled so increase round size */
+ c->round_size += c->device_size;
+
+ if( nwipe_options.verify == NWIPE_VERIFY_LAST || nwipe_options.verify == NWIPE_VERIFY_ALL )
+ {
+ c->round_size += c->device_size;
+ }
+ }
+ else
+ {
+ /* Blanking not enabled, check for 'Verify_last', increase round size if enabled. */
+ if( nwipe_options.verify == NWIPE_VERIFY_LAST )
+ {
+ c->round_size += c->device_size;
+ }
+ }
+
+ /* Additional method specific round_size adjustments go in this switch statement */
+
+ switch( selected_method )
+ {
+ case 0:
+ /* NWIPE_ZERO - No additional calculation required
+ * ---------- */
+ break;
+
+ case 1:
+ /* NWIPE_OPS2
+ * ---------- */
+
+ /* Required for mandatory 9th and final random pass */
+ c->round_size += c->device_size;
+
+ /* Required for selectable 9th and final random verification */
+ if( nwipe_options.verify == NWIPE_VERIFY_ALL || nwipe_options.verify == NWIPE_VERIFY_LAST )
+ {
+ c->round_size += c->device_size;
+ }
+
+ /* As no final zero blanking pass is permitted by this standard reduce round size if it's selected */
+ if( nwipe_options.noblank == 0 )
+ {
+ /* Reduce for blanking pass */
+ c->round_size -= c->device_size;
+
+ /* Reduce for blanking pass verification */
+ if( nwipe_options.verify == NWIPE_VERIFY_ALL || nwipe_options.verify == NWIPE_VERIFY_LAST )
+ {
+ c->round_size -= c->device_size;
+ }
+ }
+ else
+ {
+ if( nwipe_options.verify == NWIPE_VERIFY_LAST )
+ {
+ /* If blanking off & verification on reduce round size */
+ c->round_size -= c->device_size;
+ }
+ }
+
+ break;
+
+ case 2:
+ /* DoD Short - No additional calculation required
+ * --------- */
+
+ break;
+
+ case 3:
+ /* DOD 522022m - No additional calculation required
+ * ----------- */
+
+ break;
+
+ case 4:
+ /* GutMann - No additional calculation required
+ * ------- */
+
+ break;
+
+ case 5:
+ /* PRNG (random) - No additional calculation required
+ * ------------- */
+
+ break;
+
+ case 6:
+ /* NWIPE_IS5ENH
+ * ------------ */
+
+ /* This method ALWAYS verifies the 3rd pass so increase by device size,
+ * but NOT if VERIFY_ALL has been selected, but first .. */
+
+ /* Reduce as Verify_Last already included previously if blanking was off */
+ if( nwipe_options.verify == NWIPE_VERIFY_LAST && nwipe_options.noblank == 1 )
+ {
+ c->round_size -= c->device_size;
+ }
+
+ /* Adjusts for verify on every third pass multiplied by number of rounds */
+ if( nwipe_options.verify != NWIPE_VERIFY_ALL )
+ {
+ c->round_size += ( c->device_size * c->round_count );
+ }
+
+ break;
+ }
+}
+
+/* eof */
diff --git a/src/method.h b/src/method.h
new file mode 100644
index 0000000..f6fdbc2
--- /dev/null
+++ b/src/method.h
@@ -0,0 +1,60 @@
+/*
+ * methods.c: Method implementations 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.
+ *
+ */
+
+#ifndef METHOD_H_
+#define METHOD_H_
+
+/* The argument list for nwipe methods. */
+#define NWIPE_METHOD_SIGNATURE nwipe_context_t* c
+
+typedef enum nwipe_verify_t_ {
+ NWIPE_VERIFY_NONE = 0, // Do not read anything back from the device.
+ NWIPE_VERIFY_LAST, // Check the last pass.
+ NWIPE_VERIFY_ALL, // Check all passes.
+} nwipe_verify_t;
+
+/* The typedef of the function that will do the wipe. */
+typedef int ( *nwipe_method_t )( void* ptr );
+
+typedef struct
+{
+ int length; // Length of the pattern in bytes, -1 means random.
+ char* s; // The actual bytes of the pattern.
+} nwipe_pattern_t;
+
+const char* nwipe_method_label( void* method );
+int nwipe_runmethod( NWIPE_METHOD_SIGNATURE, nwipe_pattern_t* patterns );
+
+void* nwipe_dod522022m( void* ptr );
+void* nwipe_dodshort( void* ptr );
+void* nwipe_gutmann( void* ptr );
+void* nwipe_ops2( void* ptr );
+void* nwipe_is5enh( void* ptr );
+void* nwipe_random( void* ptr );
+void* nwipe_zero( void* ptr );
+void* nwipe_one( void* ptr );
+void* nwipe_verify_zero( void* ptr );
+void* nwipe_verify_one( void* ptr );
+
+void calculate_round_size( nwipe_context_t* );
+
+#endif /* METHOD_H_ */
diff --git a/src/mt19937ar-cok/mt19937ar-cok.c b/src/mt19937ar-cok/mt19937ar-cok.c
new file mode 100644
index 0000000..63ef659
--- /dev/null
+++ b/src/mt19937ar-cok/mt19937ar-cok.c
@@ -0,0 +1,139 @@
+/*
+ This code is modified for use in nwipe.
+
+ A C-program for MT19937, with initialization improved 2002/2/10.
+ Coded by Takuji Nishimura and Makoto Matsumoto.
+ This is a faster version by taking Shawn Cokus's optimization,
+ Matthe Bellew's simplification, Isaku Wada's real version.
+
+ Before using, initialize the state by using init_genrand(seed)
+ or init_by_array(init_key, key_length).
+
+ Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. The names of its contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+ Any feedback is very welcome.
+ http://www.math.keio.ac.jp/matumoto/emt.html
+ email: matumoto@math.keio.ac.jp
+*/
+
+#include <stdio.h>
+#include "mt19937ar-cok.h"
+
+/* initializes state[N] with a seed */
+void init_genrand( twister_state_t* state, unsigned long s)
+{
+ int j;
+ state->array[0]= s & 0xffffffffUL;
+ for( j = 1; j < N; j++ )
+ {
+ state->array[j] = (1812433253UL * (state->array[j-1] ^ (state->array[j-1] >> 30)) + j);
+ state->array[j] &= 0xffffffffUL; /* for >32 bit machines */
+ }
+ state->left = 1;
+ state->initf = 1;
+}
+
+
+void twister_init( twister_state_t* state, unsigned long init_key[], unsigned long key_length )
+{
+ int i = 1;
+ int j = 0;
+ int k = ( N > key_length ? N : key_length );
+
+ init_genrand( state, 19650218UL );
+
+ for( ; k; k-- )
+ {
+ state->array[i] = (state->array[i] ^ ((state->array[i-1] ^ (state->array[i-1] >> 30)) * 1664525UL)) + init_key[j] + j;
+ state->array[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
+ ++i;
+ ++j;
+
+ if ( i >= N )
+ {
+ state->array[0] = state->array[N-1];
+ i = 1;
+ }
+
+ if ( j >= key_length )
+ {
+ j = 0;
+ }
+ }
+
+ for( k = N -1; k; k-- )
+ {
+ state->array[i] = (state->array[i] ^ ((state->array[i-1] ^ (state->array[i-1] >> 30)) * 1566083941UL)) - i;
+ state->array[i] &= 0xffffffffUL;
+ ++i;
+
+ if ( i >= N )
+ {
+ state->array[0] = state->array[N-1];
+ i = 1;
+ }
+ }
+
+ state->array[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
+ state->left = 1;
+ state->initf = 1;
+}
+
+static void next_state( twister_state_t* state )
+{
+ unsigned long *p = state->array;
+ int j;
+
+ if( state->initf == 0) { init_genrand( state, 5489UL ); }
+ state->left = N;
+ state->next = state->array;
+ for( j = N - M + 1; --j; p++ ) { *p = p[M] ^ TWIST(p[0], p[1]); }
+ for( j = M; --j; p++ ) { *p = p[M-N] ^ TWIST(p[0], p[1]); }
+ *p = p[M-N] ^ TWIST(p[0], state->array[0]);
+}
+
+/* generates a random number on [0,0xffffffff]-interval */
+unsigned long twister_genrand_int32( twister_state_t* state )
+{
+ unsigned long y;
+
+ if ( --state->left == 0 ) { next_state( state ); }
+ y = *state->next++;
+
+ /* Tempering */
+ y ^= (y >> 11);
+ y ^= (y << 7) & 0x9d2c5680UL;
+ y ^= (y << 15) & 0xefc60000UL;
+ y ^= (y >> 18);
+
+ return y;
+}
diff --git a/src/mt19937ar-cok/mt19937ar-cok.h b/src/mt19937ar-cok/mt19937ar-cok.h
new file mode 100644
index 0000000..afcc873
--- /dev/null
+++ b/src/mt19937ar-cok/mt19937ar-cok.h
@@ -0,0 +1,32 @@
+/*
+ * mt19937ar-cok.h: The Mersenne Twister PRNG implementation for nwipe.
+ *
+ */
+
+#ifndef MT19937AR_H_
+#define MT19937AR_H_
+
+/* Period parameters */
+#define N 624
+#define M 397
+#define MATRIX_A 0x9908b0dfUL /* constant vector a */
+#define UMASK 0x80000000UL /* most significant w-r bits */
+#define LMASK 0x7fffffffUL /* least significant r bits */
+#define MIXBITS(u,v) ( ((u) & UMASK) | ((v) & LMASK) )
+#define TWIST(u,v) ((MIXBITS(u,v) >> 1) ^ ((v)&1UL ? MATRIX_A : 0UL))
+
+typedef struct twister_state_t_
+{
+ unsigned long array[N];
+ int left;
+ int initf;
+ unsigned long *next;
+} twister_state_t;
+
+/* Initialize the MT state. ( 0 < key_length <= 624 ). */
+void twister_init( twister_state_t* state, unsigned long init_key[], unsigned long key_length);
+
+/* Generate a random integer on the [0,0xffffffff] interval. */
+unsigned long twister_genrand_int32( twister_state_t* state );
+
+#endif /* MT19937AR_H_ */
diff --git a/src/nwipe.c b/src/nwipe.c
new file mode 100644
index 0000000..d5f3798
--- /dev/null
+++ b/src/nwipe.c
@@ -0,0 +1,982 @@
+/*
+ * nwipe.c: Darik's Wipe.
+ *
+ * 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.
+ *
+ */
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+
+#ifndef _POSIX_SOURCE
+#define _POSIX_SOURCE
+#endif
+
+#include <stdint.h>
+#include <time.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/types.h>
+
+#include "nwipe.h"
+#include "context.h"
+#include "method.h"
+#include "prng.h"
+#include "options.h"
+#include "device.h"
+#include "logging.h"
+#include "gui.h"
+#include "temperature.h"
+
+#include <sys/ioctl.h> /* FIXME: Twice Included */
+#include <sys/shm.h>
+#include <wait.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include "version.h"
+
+int terminate_signal;
+int user_abort;
+int global_wipe_status;
+
+int main( int argc, char** argv )
+{
+ int nwipe_optind; // The result of nwipe_options().
+ int nwipe_enumerated; // The number of contexts that have been enumerated.
+ int nwipe_error = 0; // An error counter.
+ int nwipe_selected = 0; // The number of contexts that have been selected.
+ int any_threads_still_running; // used in wipe thread cancellation wait loop
+ int thread_timeout_counter; // timeout thread cancellation after THREAD_CANCELLATION_TIMEOUT seconds
+ pthread_t nwipe_gui_thread = 0; // The thread ID of the GUI thread.
+ pthread_t nwipe_sigint_thread; // The thread ID of the sigint handler.
+
+ char modprobe_command[] = "modprobe %s";
+ char modprobe_command2[] = "/sbin/modprobe %s";
+ char modprobe_command3[] = "/usr/sbin/modprobe %s";
+ char module_shortform[50];
+ char final_cmd_modprobe[sizeof( modprobe_command ) + sizeof( module_shortform )];
+
+ /* The entropy source file handle. */
+ int nwipe_entropy;
+
+ /* The generic index variables. */
+ int i;
+ int j;
+
+ /* The generic result buffer. */
+ int r;
+
+ /* Log nwipes version */
+ nwipe_log( NWIPE_LOG_INFO, "%s", banner );
+
+ /* Log OS info */
+ nwipe_log_OSinfo();
+
+ /* Initialise the termintaion signal, 1=terminate nwipe */
+ terminate_signal = 0;
+
+ /* Initialise the user abort signal, 1=User aborted with CNTRL-C,SIGTERM, SIGQUIT, SIGINT etc.. */
+ user_abort = 0;
+
+ /* nwipes return status value, set prior to exit at the end of nwipe, as no other exit points allowed */
+ int return_status = 0;
+
+ /* Initialise, flag indicating whether a wipe has actually started or not 0=no, 1=yes */
+ global_wipe_status = 0;
+
+ /* Initialise flags that indicate whether a fatal or non fatal error occurred on ANY drive */
+ int fatal_errors_flag = 0;
+ int non_fatal_errors_flag = 0;
+
+ /* Two arrays are used, containing pointers to the the typedef for each disk */
+ /* The first array (c1) points to all devices, the second points to only */
+ /* the disks selected for wiping. */
+
+ /* The array of pointers to enumerated contexts. */
+ /* Initialised and populated in device scan. */
+ nwipe_context_t** c1 = 0;
+
+ int wipe_threads_started = 0;
+
+ /* Parse command line options. */
+ nwipe_optind = nwipe_options_parse( argc, argv );
+
+ if( nwipe_optind == argc )
+ {
+ /* File names were not given by the user. Scan for devices. */
+ nwipe_enumerated = nwipe_device_scan( &c1 );
+
+ if( terminate_signal == 1 )
+ {
+ cleanup();
+ exit( 1 );
+ }
+
+ if( nwipe_enumerated == 0 )
+ {
+ nwipe_log( NWIPE_LOG_INFO,
+ "Storage devices not found. Nwipe should be run as root or sudo/su, i.e sudo nwipe etc" );
+ cleanup();
+ return -1;
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Automatically enumerated %i devices.", nwipe_enumerated );
+ }
+ }
+ else
+ {
+ argv += nwipe_optind;
+ argc -= nwipe_optind;
+
+ nwipe_enumerated = nwipe_device_get( &c1, argv, argc );
+ if( nwipe_enumerated == 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Devices not found. Check you're not excluding drives unnecessarily." );
+ printf( "No drives found\n" );
+ cleanup();
+ exit( 1 );
+ }
+ }
+
+ if( terminate_signal == 1 )
+ {
+ cleanup();
+ exit( 1 );
+ }
+
+ /* Log the System information */
+ nwipe_log_sysinfo();
+
+ /* The array of pointers to contexts that will actually be wiped. */
+ nwipe_context_t** c2 = (nwipe_context_t**) malloc( nwipe_enumerated * sizeof( nwipe_context_t* ) );
+
+ /* Open the entropy source. */
+ nwipe_entropy = open( NWIPE_KNOB_ENTROPY, O_RDONLY );
+
+ /* Check the result. */
+ if( nwipe_entropy < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "open" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to open entropy source %s.", NWIPE_KNOB_ENTROPY );
+ cleanup();
+ free( c2 );
+ return errno;
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE, "Opened entropy source '%s'.", NWIPE_KNOB_ENTROPY );
+
+ /* Block relevant signals in main thread. Any other threads that are */
+ /* created after this will also block those signals. */
+ sigset_t sigset;
+ sigemptyset( &sigset );
+ sigaddset( &sigset, SIGHUP );
+ sigaddset( &sigset, SIGTERM );
+ sigaddset( &sigset, SIGQUIT );
+ sigaddset( &sigset, SIGINT );
+ sigaddset( &sigset, SIGUSR1 );
+ pthread_sigmask( SIG_SETMASK, &sigset, NULL );
+
+ /* Create a signal handler thread. This thread will catch all */
+ /* signals and decide what to do with them. This will only */
+ /* catch nondirected signals. (I.e., if a thread causes a SIGFPE */
+ /* then that thread will get that signal. */
+
+ /* Pass a pointer to a struct containing all data to the signal handler. */
+ nwipe_misc_thread_data_t nwipe_misc_thread_data;
+ nwipe_thread_data_ptr_t nwipe_thread_data_ptr;
+
+ nwipe_thread_data_ptr.c = c2;
+ nwipe_misc_thread_data.nwipe_enumerated = nwipe_enumerated;
+ nwipe_misc_thread_data.nwipe_selected = 0;
+ if( !nwipe_options.nogui )
+ nwipe_misc_thread_data.gui_thread = &nwipe_gui_thread;
+ nwipe_thread_data_ptr.nwipe_misc_thread_data = &nwipe_misc_thread_data;
+
+ if( !nwipe_options.nosignals )
+ {
+ pthread_attr_t pthread_attr;
+ pthread_attr_init( &pthread_attr );
+ pthread_attr_setdetachstate( &pthread_attr, PTHREAD_CREATE_DETACHED );
+
+ pthread_create( &nwipe_sigint_thread, &pthread_attr, signal_hand, &nwipe_thread_data_ptr );
+ }
+
+ /* Makesure the drivetemp module is loaded, else drives hwmon entries won't appear in /sys/class/hwmon */
+ final_cmd_modprobe[0] = 0;
+
+ /* The kernel module we are going to load */
+ strcpy( module_shortform, "drivetemp" );
+
+ /* Determine whether we can access modprobe, required if the PATH environment is not setup ! (Debian sid 'su' as
+ * opposed to 'su -' */
+
+ if( system( "which modprobe > /dev/null 2>&1" ) )
+ {
+ if( system( "which /sbin/modprobe > /dev/null 2>&1" ) )
+ {
+ if( system( "which /usr/sbin/modprobe > /dev/null 2>&1" ) )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "modprobe command not found. Install kmod package (modprobe)) !" );
+ nwipe_log( NWIPE_LOG_WARNING, "Most temperature monitoring may be unavailable as module drivetemp" );
+ nwipe_log( NWIPE_LOG_WARNING, "could not be loaded. drivetemp is not available on kernels < v5.5" );
+ }
+ else
+ {
+ sprintf( final_cmd_modprobe, modprobe_command3, module_shortform );
+ }
+ }
+ else
+ {
+ sprintf( final_cmd_modprobe, modprobe_command2, module_shortform );
+ }
+ }
+ else
+ {
+ sprintf( final_cmd_modprobe, modprobe_command, module_shortform );
+ }
+
+ /* load the drivetemp module */
+ if( system( final_cmd_modprobe ) != 0 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "hwmon: Unable to load module drivetemp, temperatures may be unavilable." );
+ nwipe_log( NWIPE_LOG_WARNING, "hwmon: It's possible the drivetemp software isn't modular but built-in" );
+ nwipe_log( NWIPE_LOG_WARNING, "hwmon: to the kernel, as is the case with ShredOS.x86_64 in which case" );
+ nwipe_log( NWIPE_LOG_WARNING, "hwmon: the temperatures will actually be available despite this issue." );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "hwmon: Module drivetemp loaded, drive temperatures available" );
+ }
+
+ /* A context struct for each device has already been created. */
+ /* Now set specific nwipe options */
+ for( i = 0; i < nwipe_enumerated; i++ )
+ {
+
+ /* Set the entropy source. */
+ c1[i]->entropy_fd = nwipe_entropy;
+
+ if( nwipe_options.autonuke )
+ {
+ /* When the autonuke option is set, select all disks. */
+ // TODO - partitions
+ // if( c1[i].device_part == 0 ) { c1[i].select = NWIPE_SELECT_TRUE; }
+ // else { c1[i].select = NWIPE_SELECT_TRUE_PARENT; }
+ c1[i]->select = NWIPE_SELECT_TRUE;
+ }
+ else
+ {
+ /* The user must manually select devices. */
+ c1[i]->select = NWIPE_SELECT_FALSE;
+ }
+
+ /* Initialise temperature variables for device */
+ nwipe_init_temperature( c1[i] );
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "hwmon: Device %s hwmon path = %s", c1[i]->device_name, c1[i]->temp1_path );
+ }
+
+ nwipe_update_temperature( c1[i] );
+ }
+
+ /* Check for initialization errors. */
+ if( nwipe_error )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "Initialization error %i\n", nwipe_error );
+ cleanup();
+ return -1;
+ }
+
+ /* Start the ncurses interface. */
+ if( !nwipe_options.nogui )
+ nwipe_gui_init();
+
+ if( nwipe_options.autonuke == 1 )
+ {
+ /* Print the options window. */
+ if( !nwipe_options.nogui )
+ nwipe_gui_options();
+ }
+ else
+ {
+ /* Get device selections from the user. */
+ if( nwipe_options.nogui )
+ {
+ printf( "--nogui option must be used with autonuke option\n" );
+ cleanup();
+ exit( 1 );
+ }
+ else
+ {
+ nwipe_gui_select( nwipe_enumerated, c1 );
+ }
+ }
+
+ for( i = 0; i < nwipe_enumerated; i++ )
+ {
+ /* Set the PRNG implementation, which must always come after the function nwipe_gui_select ! */
+ c1[i]->prng = nwipe_options.prng;
+ c1[i]->prng_seed.length = 0;
+ c1[i]->prng_seed.s = 0;
+ c1[i]->prng_state = 0;
+
+ /* Count the number of selected contexts. */
+ if( c1[i]->select == NWIPE_SELECT_TRUE )
+ {
+ nwipe_selected += 1;
+ }
+
+ /* Initialise the wipe result value */
+ c1[i]->result = 0;
+ }
+
+ /* Pass the number selected to the struct for other threads */
+ nwipe_misc_thread_data.nwipe_selected = nwipe_selected;
+
+ /* Populate the array of selected contexts. */
+ for( i = 0, j = 0; i < nwipe_enumerated; i++ )
+ {
+ if( c1[i]->select == NWIPE_SELECT_TRUE )
+ {
+ /* Copy the context. */
+ c2[j++] = c1[i];
+ }
+ }
+
+ /* TODO: free c1 and c2 memory. */
+ if( user_abort == 0 )
+ {
+ /* Log the wipe options that have been selected immediately prior to the start of the wipe */
+ nwipe_options_log();
+
+ /* The wipe has been initiated */
+ global_wipe_status = 1;
+
+ for( i = 0; i < nwipe_selected; i++ )
+ {
+ /* A result buffer for the BLKGETSIZE64 ioctl. */
+ u64 size64;
+
+ /* Initialise the spinner character index */
+ c2[i]->spinner_idx = 0;
+
+ /* Initialise the start and end time of the wipe */
+ c2[i]->start_time = 0;
+ c2[i]->end_time = 0;
+
+ /* Initialise the wipe_status flag, -1 = wipe not yet started */
+ c2[i]->wipe_status = -1;
+
+ /* Open the file for reads and writes. */
+ c2[i]->device_fd = open( c2[i]->device_name, O_RDWR );
+
+ /* Check the open() result. */
+ if( c2[i]->device_fd < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "open" );
+ nwipe_log( NWIPE_LOG_WARNING, "Unable to open device '%s'.", c2[i]->device_name );
+ c2[i]->select = NWIPE_SELECT_DISABLED;
+ continue;
+ }
+
+ /* Stat the file. */
+ if( fstat( c2[i]->device_fd, &c2[i]->device_stat ) != 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "fstat" );
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to stat file '%s'.", c2[i]->device_name );
+ nwipe_error++;
+ continue;
+ }
+
+ /* Check that the file is a block device. */
+ if( !S_ISBLK( c2[i]->device_stat.st_mode ) )
+ {
+ nwipe_log( NWIPE_LOG_ERROR, "'%s' is not a block device.", c2[i]->device_name );
+ nwipe_error++;
+ continue;
+ }
+
+ /* TODO: Lock the file for exclusive access. */
+ /*
+ if( flock( c2[i]->device_fd, LOCK_EX | LOCK_NB ) != 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "flock" );
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to lock the '%s' file.", c2[i]->device_name );
+ nwipe_error++;
+ continue;
+ }
+ */
+
+ /* Print serial number of device if it exists. */
+ if( strlen( (const char*) c2[i]->device_serial_no ) )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "%s has serial number %s", c2[i]->device_name, c2[i]->device_serial_no );
+ }
+
+ /* Do sector size and block size checking. */
+ if( ioctl( c2[i]->device_fd, BLKSSZGET, &c2[i]->device_sector_size ) == 0 )
+ {
+
+ if( ioctl( c2[i]->device_fd, BLKBSZGET, &c2[i]->device_block_size ) != 0 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Device '%s' failed BLKBSZGET ioctl.", c2[i]->device_name );
+ c2[i]->device_block_size = 0;
+ }
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "Device '%s' failed BLKSSZGET ioctl.", c2[i]->device_name );
+ c2[i]->device_sector_size = 0;
+ c2[i]->device_block_size = 0;
+ }
+
+ /* The st_size field is zero for block devices. */
+ /* ioctl( c2[i]->device_fd, BLKGETSIZE64, &c2[i]->device_size ); */
+
+ /* Seek to the end of the device to determine its size. */
+ c2[i]->device_size = lseek( c2[i]->device_fd, 0, SEEK_END );
+
+ /* Also ask the driver for the device size. */
+ /* if( ioctl( c2[i]->device_fd, BLKGETSIZE64, &size64 ) ) */
+ if( ioctl( c2[i]->device_fd, _IOR( 0x12, 114, size_t ), &size64 ) )
+ {
+ /* The ioctl failed. */
+ fprintf( stderr, "Error: BLKGETSIZE64 failed on '%s'.\n", c2[i]->device_name );
+ nwipe_log( NWIPE_LOG_ERROR, "BLKGETSIZE64 failed on '%s'.\n", c2[i]->device_name );
+ nwipe_error++;
+ }
+ c2[i]->device_size = size64;
+
+ /* Check whether the two size values agree. */
+ if( c2[i]->device_size != size64 )
+ {
+ /* This could be caused by the linux last-odd-block problem. */
+ fprintf( stderr, "Error: Last-odd-block detected on '%s'.\n", c2[i]->device_name );
+ nwipe_log( NWIPE_LOG_ERROR, "Last-odd-block detected on '%s'.", c2[i]->device_name );
+ nwipe_error++;
+ }
+
+ if( c2[i]->device_size == (long long) -1 )
+ {
+ /* We cannot determine the size of this device. */
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to determine the size of '%s'.", c2[i]->device_name );
+ nwipe_error++;
+ }
+ else
+ {
+ /* Reset the file pointer. */
+ r = lseek( c2[i]->device_fd, 0, SEEK_SET );
+
+ if( r == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to reset the '%s' file offset.", c2[i]->device_name );
+ nwipe_error++;
+ }
+ }
+
+ if( c2[i]->device_size == 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "%s, sect/blk/dev %i/%i/%llu",
+ c2[i]->device_name,
+ c2[i]->device_sector_size,
+ c2[i]->device_block_size,
+ c2[i]->device_size );
+ nwipe_error++;
+ continue;
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE,
+ "%s, sect/blk/dev %i/%i/%llu",
+ c2[i]->device_name,
+ c2[i]->device_sector_size,
+ c2[i]->device_block_size,
+ c2[i]->device_size );
+ }
+
+ /* Fork a child process. */
+ errno = pthread_create( &c2[i]->thread, NULL, nwipe_options.method, (void*) c2[i] );
+ if( errno )
+ {
+ nwipe_perror( errno, __FUNCTION__, "pthread_create" );
+ if( !nwipe_options.nogui )
+ nwipe_gui_free();
+ return errno;
+ }
+ else
+ {
+ wipe_threads_started = 1;
+ }
+ }
+ }
+
+ /* Change the terminal mode to non-blocking input. */
+ nodelay( stdscr, 0 );
+
+ /* Set getch to delay in order to slow screen updates. */
+ halfdelay( NWIPE_KNOB_SLEEP * 10 );
+
+ /* Set up data structs to pass the GUI thread the data it needs. */
+ nwipe_thread_data_ptr_t nwipe_gui_data;
+ if( !nwipe_options.nogui )
+ {
+ nwipe_gui_data.c = c2;
+ nwipe_gui_data.nwipe_misc_thread_data = &nwipe_misc_thread_data;
+ /* Fork the GUI thread. */
+ errno = pthread_create( &nwipe_gui_thread, NULL, nwipe_gui_status, &nwipe_gui_data );
+ }
+
+ /* Wait for all the wiping threads to finish, but don't wait if we receive the terminate signal */
+
+ /* set getch delay to 2/10th second. */
+ halfdelay( 10 );
+
+ i = 0;
+ while( i < nwipe_selected && terminate_signal == 0 )
+ {
+ if( i == nwipe_selected )
+ {
+ break;
+ }
+
+ if( c2[i]->wipe_status != 0 )
+ {
+ i = 0;
+ }
+ else
+ {
+ i++;
+ continue;
+ }
+ sleep( 1 ); /* DO NOT REMOVE ! Stops the routine hogging CPU cycles */
+ }
+
+ if( terminate_signal != 1 )
+ {
+ if( !nwipe_options.nowait && !nwipe_options.autopoweroff )
+ {
+ do
+ {
+ sleep( 1 );
+
+ } while( terminate_signal != 1 );
+ }
+ }
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Exit in progress" );
+ }
+ /* Send a REQUEST for the wipe threads to be cancelled */
+ for( i = 0; i < nwipe_selected; i++ )
+ {
+
+ if( c2[i]->thread )
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Requesting wipe thread cancellation for %s", c2[i]->device_name );
+ }
+ pthread_cancel( c2[i]->thread );
+ }
+ }
+
+ /* Kill the GUI thread */
+ if( nwipe_gui_thread )
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Cancelling the GUI thread." );
+ }
+
+ /* We don't want to use pthread_cancel as our GUI thread is aware of the control-c
+ * signal and will exit itself we just join the GUI thread and wait for confirmation
+ */
+ r = pthread_join( nwipe_gui_thread, NULL );
+ if( r != 0 )
+ {
+ nwipe_log( NWIPE_LOG_WARNING, "main()>pthread_join():Error when waiting for GUI thread to cancel." );
+ }
+ else
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "GUI compute_stats thread has been cancelled" );
+ }
+ }
+ }
+
+ /* Release the gui. */
+ if( !nwipe_options.nogui )
+ {
+ nwipe_gui_free();
+ }
+
+ /* Now join the wipe threads and wait until they have terminated */
+ any_threads_still_running = 1;
+ thread_timeout_counter = THREAD_CANCELLATION_TIMEOUT;
+ while( any_threads_still_running )
+ {
+ /* quit waiting if we've tried 'thread_timeout_counter' times */
+ if( thread_timeout_counter == 0 )
+ {
+ break;
+ }
+
+ any_threads_still_running = 0;
+ for( i = 0; i < nwipe_selected; i++ )
+ {
+ if( c2[i]->thread )
+ {
+ printf( "\nWaiting for wipe thread to cancel for %s\n", c2[i]->device_name );
+
+ /* Joins the thread and waits for completion before continuing */
+ r = pthread_join( c2[i]->thread, NULL );
+ if( r != 0 )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Error joining the wipe thread when waiting for thread to cancel.",
+ c2[i]->device_name );
+
+ if( r == EDEADLK )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Error joining the wipe thread: EDEADLK: Deadlock detected.",
+ c2[i]->device_name );
+ }
+ else
+ {
+ if( r == EINVAL )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Error joining the wipe thread: %s EINVAL: thread is not joinable.",
+ c2[i]->device_name );
+ }
+ else
+ {
+ if( r == ESRCH )
+ {
+ nwipe_log( NWIPE_LOG_ERROR,
+ "Error joining the wipe thread: %s ESRCH: no matching thread found",
+ c2[i]->device_name );
+ }
+ }
+ }
+
+ any_threads_still_running = 1;
+ }
+ else
+ {
+ c2[i]->thread = 0; /* Zero the thread so we know it's been cancelled */
+
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Wipe thread for device %s has terminated", c2[i]->device_name );
+ }
+
+ /* Close the device file descriptor. */
+ close( c2[i]->device_fd );
+ }
+ }
+ }
+ thread_timeout_counter--;
+ sleep( 1 );
+ }
+ if( nwipe_options.verbose )
+ {
+ for( i = 0; i < nwipe_selected; i++ )
+ {
+ nwipe_log( NWIPE_LOG_DEBUG,
+ "Status: %s, result=%d, pass_errors=%llu, verify_errors=%llu, fsync_errors=%llu",
+ c2[i]->device_name,
+ c2[i]->result,
+ c2[i]->pass_errors,
+ c2[i]->verify_errors,
+ c2[i]->fsyncdata_errors );
+ }
+ }
+
+ /* if no wipe threads started then zero each selected drive result flag,
+ * as we don't need to report fatal/non fatal errors if no wipes were ever started ! */
+ if( wipe_threads_started == 0 )
+ {
+ for( i = 0; i < nwipe_selected; i++ )
+ {
+ c2[i]->result = 0;
+ }
+ }
+ else
+ {
+ for( i = 0; i < nwipe_selected; i++ )
+ {
+ /* Check for errors. */
+ if( c2[i]->result != 0 || c2[i]->pass_errors != 0 || c2[i]->verify_errors != 0
+ || c2[i]->fsyncdata_errors != 0 )
+ {
+ /* If the run_method ever returns anything other than zero then makesure there is at least one pass
+ * error This is so that the log summary tables correctly show a failure when one occurs as it only
+ * shows pass, verification and fdatasync errors. */
+ if( c2[i]->result != 0 )
+ {
+ if( c2[i]->pass_errors == 0 )
+ {
+ c2[i]->pass_errors = 1;
+ }
+ }
+
+ nwipe_log( NWIPE_LOG_FATAL,
+ "Nwipe exited with errors on device = %s, see log for specific error\n",
+ c2[i]->device_name );
+ nwipe_log( NWIPE_LOG_DEBUG,
+ "Status: %s, result=%d, pass_errors=%llu, verify_errors=%llu, fsync_errors=%llu",
+ c2[i]->device_name,
+ c2[i]->result,
+ c2[i]->pass_errors,
+ c2[i]->verify_errors,
+ c2[i]->fsyncdata_errors );
+ non_fatal_errors_flag = 1;
+ return_status = 1;
+ }
+ }
+ }
+
+ /* Generate and send the drive status summary to the log */
+ nwipe_log_summary( c2, nwipe_selected );
+
+ /* Print a one line status message for the user */
+ if( return_status == 0 || return_status == 1 )
+ {
+ if( user_abort == 1 )
+ {
+ if( global_wipe_status == 1 )
+ {
+ nwipe_log( NWIPE_LOG_INFO,
+ "Nwipe was aborted by the user. Check the summary table for the drive status." );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Nwipe was aborted by the user prior to the wipe starting." );
+ }
+ }
+ else
+ {
+ if( fatal_errors_flag == 1 || non_fatal_errors_flag == 1 )
+ {
+ nwipe_log( NWIPE_LOG_INFO,
+ "Nwipe exited with errors, check the log & summary table for individual drive status." );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Nwipe successfully completed. See summary table for details." );
+ }
+ }
+ }
+
+ cleanup();
+
+ check_for_autopoweroff();
+
+ /* Exit. */
+ return return_status;
+}
+
+void* signal_hand( void* ptr )
+{
+ int sig;
+ int hours;
+ int minutes;
+ int seconds;
+
+ hours = 0;
+ minutes = 0;
+ seconds = 0;
+
+ // Define signals that this handler should react to
+ sigset_t sigset;
+ sigemptyset( &sigset );
+ sigaddset( &sigset, SIGHUP );
+ sigaddset( &sigset, SIGTERM );
+ sigaddset( &sigset, SIGQUIT );
+ sigaddset( &sigset, SIGINT );
+ sigaddset( &sigset, SIGUSR1 );
+
+ int i;
+ char eta[9];
+
+ /* Set up the structs we will use for the data required. */
+ nwipe_thread_data_ptr_t* nwipe_thread_data_ptr;
+ nwipe_context_t** c;
+ nwipe_misc_thread_data_t* nwipe_misc_thread_data;
+
+ /* Retrieve from the pointer passed to the function. */
+ nwipe_thread_data_ptr = (nwipe_thread_data_ptr_t*) ptr;
+ c = nwipe_thread_data_ptr->c;
+ nwipe_misc_thread_data = nwipe_thread_data_ptr->nwipe_misc_thread_data;
+
+ while( 1 )
+ {
+ /* wait for a signal to arrive */
+ sigwait( &sigset, &sig );
+
+ switch( sig )
+ {
+
+ // Log current status. All values are automatically updated by the GUI
+ case SIGUSR1:
+ compute_stats( ptr );
+
+ for( i = 0; i < nwipe_misc_thread_data->nwipe_selected; i++ )
+ {
+
+ if( c[i]->thread )
+ {
+ char* status = "";
+ switch( c[i]->pass_type )
+ {
+ case NWIPE_PASS_FINAL_BLANK:
+ status = "[blanking]";
+ break;
+
+ case NWIPE_PASS_FINAL_OPS2:
+ status = "[OPS-II final]";
+ break;
+
+ case NWIPE_PASS_WRITE:
+ status = "[writing]";
+ break;
+
+ case NWIPE_PASS_VERIFY:
+ status = "[verifying]";
+ break;
+
+ case NWIPE_PASS_NONE:
+ break;
+ }
+ if( c[i]->sync_status )
+ {
+ status = "[syncing]";
+ }
+
+ convert_seconds_to_hours_minutes_seconds( c[i]->eta, &hours, &minutes, &seconds );
+
+ nwipe_log( NWIPE_LOG_INFO,
+ "%s: %05.2f%%, round %i of %i, pass %i of %i, eta %02i:%02i:%02i, %s",
+ c[i]->device_name,
+ c[i]->round_percent,
+ c[i]->round_working,
+ c[i]->round_count,
+ c[i]->pass_working,
+ c[i]->pass_count,
+ hours,
+ minutes,
+ seconds,
+ status );
+ }
+ else
+ {
+ if( c[i]->result == 0 )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "%s: Success", c[i]->device_name );
+ }
+ else if( c[i]->signal )
+ {
+ nwipe_log(
+ NWIPE_LOG_INFO, "%s: >>> FAILURE! <<<: signal %i", c[i]->device_name, c[i]->signal );
+ }
+ else
+ {
+ nwipe_log(
+ NWIPE_LOG_INFO, "%s: >>> FAILURE! <<<: code %i", c[i]->device_name, c[i]->result );
+ }
+ }
+ }
+
+ break;
+
+ case SIGHUP:
+ case SIGINT:
+ case SIGQUIT:
+ case SIGTERM:
+ /* Set termination flag for main() which will do housekeeping prior to exit */
+ terminate_signal = 1;
+
+ /* Set the user abort flag */
+ user_abort = 1;
+
+ /* Return control to the main thread, returning the signal received */
+ return ( (void*) 0 );
+
+ break;
+ }
+ }
+
+ return ( 0 );
+}
+
+int cleanup()
+{
+ int i;
+ extern int log_elements_displayed;
+ extern int log_elements_allocated;
+ extern char** log_lines;
+
+ /* Print the logs held in memory. */
+ for( i = log_elements_displayed; i < log_elements_allocated; i++ )
+ {
+ printf( "%s\n", log_lines[i] );
+ }
+ fflush( stdout );
+
+ /* Deallocate memory used by logging */
+ if( log_elements_allocated != 0 )
+ {
+ for( i = 0; i < log_elements_allocated; i++ )
+ {
+ free( log_lines[i] );
+ }
+ log_elements_allocated = 0; // zeroed just in case cleanup is called twice.
+ free( log_lines );
+ }
+
+ /* TODO: All other cleanup required */
+
+ return 0;
+}
+void check_for_autopoweroff( void )
+{
+ char cmd[] = "shutdown -Ph +1 \"System going down in one minute\"";
+ FILE* fp;
+ int r; // A result buffer.
+
+ /* User request auto power down ? */
+ if( nwipe_options.autopoweroff == 1 )
+ {
+ fp = popen( cmd, "r" );
+ if( fp == NULL )
+ {
+ nwipe_log( NWIPE_LOG_INFO, "Failed to autopoweroff to %s", cmd );
+ return;
+ }
+ r = pclose( fp );
+ }
+}
diff --git a/src/nwipe.h b/src/nwipe.h
new file mode 100644
index 0000000..73f6f99
--- /dev/null
+++ b/src/nwipe.h
@@ -0,0 +1,114 @@
+/*.
+ * nwipe.h: The header file of the nwipe program.
+ *
+ * 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.
+ *
+ */
+
+#ifndef NWIPE_H_
+#define NWIPE_H_
+
+/* Function prototypes */
+int cleanup();
+void check_for_autopoweroff( void );
+void* signal_hand( void* );
+
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64
+#endif
+
+/* workaround for Fedora */
+#ifndef off64_t
+#ifndef off_t
+#define off64_t int64_t
+#else
+#define off64_t off_t
+#endif
+#endif
+
+/* Busybox headers. */
+#ifdef BB_VER
+#include "busybox.h"
+#endif
+
+/* System headers. */
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <math.h>
+#include <pthread.h>
+#include <regex.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+/*#include "config.h"*/
+
+/* System errors. */
+extern int errno;
+
+/* 0=wipe not yet started, 1=wipe has been started by the user */
+extern int global_wipe_status;
+
+/* Ncurses headers. */
+#ifdef NCURSES_IN_SUBDIR
+#include <ncurses/ncurses.h>
+#else
+#include <ncurses.h>
+#endif
+#ifdef PANEL_IN_SUBDIR
+#include <ncurses/panel.h>
+#else
+#include <panel.h>
+#endif
+
+/* Kernel device headers. */
+#include <linux/hdreg.h>
+
+/* These types are usually defined in <asm/types.h> for __KERNEL__ code. */
+typedef unsigned long long u64;
+typedef unsigned long u32;
+typedef unsigned short u16;
+typedef unsigned char u8;
+
+/* This is required for ioctl BLKGETSIZE64, but it conflicts with <wait.h>. */
+/* #include <linux/fs.h> */
+
+/* Define ioctls that cannot be included. */
+#define BLKSSZGET _IO( 0x12, 104 )
+#define BLKBSZGET _IOR( 0x12, 112, size_t )
+#define BLKBSZSET _IOW( 0x12, 113, size_t )
+#define BLKGETSIZE64 _IOR( 0x12, 114, sizeof( u64 ) )
+
+#define THREAD_CANCELLATION_TIMEOUT 10
+
+/* This is required for ioctl FDFLUSH. */
+#include <linux/fd.h>
+
+#endif /* NWIPE_H_ */
diff --git a/src/options.c b/src/options.c
new file mode 100644
index 0000000..2930f3b
--- /dev/null
+++ b/src/options.c
@@ -0,0 +1,596 @@
+/*
+ * options.c: Command line processing routines 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.
+ *
+ */
+
+#include "nwipe.h"
+#include "context.h"
+#include "method.h"
+#include "prng.h"
+#include "options.h"
+#include "logging.h"
+#include "version.h"
+
+/* The global options struct. */
+nwipe_options_t nwipe_options;
+
+int nwipe_options_parse( int argc, char** argv )
+{
+ extern char* optarg; // The working getopt option argument.
+ extern int optind; // The working getopt index into argv.
+ extern int optopt; // The last unhandled getopt option.
+ extern int opterr; // The last getopt error number.
+
+ extern nwipe_prng_t nwipe_twister;
+ extern nwipe_prng_t nwipe_isaac;
+ extern nwipe_prng_t nwipe_isaac64;
+
+ /* The getopt() result holder. */
+ int nwipe_opt;
+
+ /* Excluded drive indexes */
+ int idx_drive_chr;
+ int idx_optarg;
+ int idx_drive;
+
+ /* Array index variable. */
+ int i;
+
+ /* The list of acceptable short options. */
+ char nwipe_options_short[] = "Vvhl:m:p:qr:e:";
+
+ /* The list of acceptable long options. */
+ static struct option nwipe_options_long[] = {
+ /* Set when the user wants to wipe without a confirmation prompt. */
+ { "autonuke", no_argument, 0, 0 },
+
+ /* Set when the user wants to have the system powerdown on completion of wipe. */
+ { "autopoweroff", no_argument, 0, 0 },
+
+ /* A GNU standard option. Corresponds to the 'h' short option. */
+ { "help", no_argument, 0, 'h' },
+
+ /* The wipe method. Corresponds to the 'm' short option. */
+ { "method", required_argument, 0, 'm' },
+
+ /* Log file. Corresponds to the 'l' short option. */
+ { "logfile", required_argument, 0, 'l' },
+
+ /* Exclude devices, comma separated list */
+ { "exclude", required_argument, 0, 'e' },
+
+ /* The Pseudo Random Number Generator. */
+ { "prng", required_argument, 0, 'p' },
+
+ /* The number of times to run the method. */
+ { "rounds", required_argument, 0, 'r' },
+
+ /* Whether to blank the disk after wiping. */
+ { "noblank", no_argument, 0, 0 },
+
+ /* Whether to ignore all USB devices. */
+ { "nousb", no_argument, 0, 0 },
+
+ /* Whether to exit after wiping or wait for a keypress. */
+ { "nowait", no_argument, 0, 0 },
+
+ /* Whether to allow signals to interrupt a wipe. */
+ { "nosignals", no_argument, 0, 0 },
+
+ /* Whether to display the gui. */
+ { "nogui", no_argument, 0, 0 },
+
+ /* Whether to anonymize the serial numbers. */
+ { "quiet", no_argument, 0, 'q' },
+
+ /* A flag to indicate whether the devices would be opened in sync mode. */
+ { "sync", required_argument, 0, 0 },
+
+ /* Verify that wipe patterns are being written to the device. */
+ { "verify", required_argument, 0, 0 },
+
+ /* Display program version. */
+ { "verbose", no_argument, 0, 'v' },
+
+ /* Display program version. */
+ { "version", no_argument, 0, 'V' },
+
+ /* Requisite padding for getopt(). */
+ { 0, 0, 0, 0 } };
+
+ /* Set default options. */
+ nwipe_options.autonuke = 0;
+ nwipe_options.autopoweroff = 0;
+ nwipe_options.method = &nwipe_dodshort;
+ nwipe_options.prng = ( sizeof( unsigned long int ) >= 8 ) ? &nwipe_isaac64 : &nwipe_isaac;
+ nwipe_options.rounds = 1;
+ nwipe_options.noblank = 0;
+ nwipe_options.nousb = 0;
+ nwipe_options.nowait = 0;
+ nwipe_options.nosignals = 0;
+ nwipe_options.nogui = 0;
+ nwipe_options.quiet = 0;
+ nwipe_options.sync = DEFAULT_SYNC_RATE;
+ nwipe_options.verbose = 0;
+ nwipe_options.verify = NWIPE_VERIFY_LAST;
+ memset( nwipe_options.logfile, '\0', sizeof( nwipe_options.logfile ) );
+
+ /* Initialise each of the strings in the excluded drives array */
+ for( i = 0; i < MAX_NUMBER_EXCLUDED_DRIVES; i++ )
+ {
+ nwipe_options.exclude[i][0] = 0;
+ }
+
+ /* Parse command line options. */
+ while( 1 )
+ {
+ /* Get the next command line option with (3)getopt. */
+ nwipe_opt = getopt_long( argc, argv, nwipe_options_short, nwipe_options_long, &i );
+
+ /* Break when we have processed all of the given options. */
+ if( nwipe_opt < 0 )
+ {
+ break;
+ }
+
+ switch( nwipe_opt )
+ {
+ case 0: /* Long options without short counterparts. */
+
+ if( strcmp( nwipe_options_long[i].name, "autonuke" ) == 0 )
+ {
+ nwipe_options.autonuke = 1;
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "autopoweroff" ) == 0 )
+ {
+ nwipe_options.autopoweroff = 1;
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "noblank" ) == 0 )
+ {
+ nwipe_options.noblank = 1;
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "nousb" ) == 0 )
+ {
+ nwipe_options.nousb = 1;
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "nowait" ) == 0 )
+ {
+ nwipe_options.nowait = 1;
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "nosignals" ) == 0 )
+ {
+ nwipe_options.nosignals = 1;
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "nogui" ) == 0 )
+ {
+ nwipe_options.nogui = 1;
+ nwipe_options.nowait = 1;
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "verbose" ) == 0 )
+ {
+ nwipe_options.verbose = 1;
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "sync" ) == 0 )
+ {
+ if( sscanf( optarg, " %i", &nwipe_options.sync ) != 1 || nwipe_options.sync < 0 )
+ {
+ fprintf( stderr, "Error: The sync argument must be a positive integer or zero.\n" );
+ exit( EINVAL );
+ }
+ break;
+ }
+
+ if( strcmp( nwipe_options_long[i].name, "verify" ) == 0 )
+ {
+
+ if( strcmp( optarg, "0" ) == 0 || strcmp( optarg, "off" ) == 0 )
+ {
+ nwipe_options.verify = NWIPE_VERIFY_NONE;
+ break;
+ }
+
+ if( strcmp( optarg, "1" ) == 0 || strcmp( optarg, "last" ) == 0 )
+ {
+ nwipe_options.verify = NWIPE_VERIFY_LAST;
+ break;
+ }
+
+ if( strcmp( optarg, "2" ) == 0 || strcmp( optarg, "all" ) == 0 )
+ {
+ nwipe_options.verify = NWIPE_VERIFY_ALL;
+ break;
+ }
+
+ /* Else we do not know this verification level. */
+ fprintf( stderr, "Error: Unknown verification level '%s'.\n", optarg );
+ exit( EINVAL );
+ }
+
+ /* getopt_long should raise on invalid option, so we should never get here. */
+ exit( EINVAL );
+
+ case 'm': /* Method option. */
+
+ if( strcmp( optarg, "dod522022m" ) == 0 || strcmp( optarg, "dod" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_dod522022m;
+ break;
+ }
+
+ if( strcmp( optarg, "dodshort" ) == 0 || strcmp( optarg, "dod3pass" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_dodshort;
+ break;
+ }
+
+ if( strcmp( optarg, "gutmann" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_gutmann;
+ break;
+ }
+
+ if( strcmp( optarg, "ops2" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_ops2;
+ break;
+ }
+
+ if( strcmp( optarg, "random" ) == 0 || strcmp( optarg, "prng" ) == 0
+ || strcmp( optarg, "stream" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_random;
+ break;
+ }
+
+ if( strcmp( optarg, "zero" ) == 0 || strcmp( optarg, "quick" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_zero;
+ break;
+ }
+
+ if( strcmp( optarg, "one" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_one;
+ break;
+ }
+
+ if( strcmp( optarg, "verify_zero" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_verify_zero;
+ break;
+ }
+
+ if( strcmp( optarg, "verify_one" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_verify_one;
+ break;
+ }
+
+ if( strcmp( optarg, "is5enh" ) == 0 )
+ {
+ nwipe_options.method = &nwipe_is5enh;
+ break;
+ }
+
+ /* Else we do not know this wipe method. */
+ fprintf( stderr, "Error: Unknown wipe method '%s'.\n", optarg );
+ exit( EINVAL );
+
+ case 'l': /* Log file option. */
+
+ nwipe_options.logfile[strlen( optarg )] = '\0';
+ strncpy( nwipe_options.logfile, optarg, sizeof( nwipe_options.logfile ) );
+ break;
+
+ case 'e': /* exclude drives option */
+
+ idx_drive_chr = 0;
+ idx_optarg = 0;
+ idx_drive = 0;
+
+ /* Create an array of excluded drives from the comma separated string */
+ while( optarg[idx_optarg] != 0 && idx_drive < MAX_NUMBER_EXCLUDED_DRIVES )
+ {
+ /* drop the leading '=' character if used */
+ if( optarg[idx_optarg] == '=' && idx_optarg == 0 )
+ {
+ idx_optarg++;
+ continue;
+ }
+
+ if( optarg[idx_optarg] == ',' )
+ {
+ /* terminate string and move onto next drive */
+ nwipe_options.exclude[idx_drive++][idx_drive_chr] = 0;
+ idx_drive_chr = 0;
+ idx_optarg++;
+ }
+ else
+ {
+ if( idx_drive_chr < MAX_DRIVE_PATH_LENGTH )
+ {
+ nwipe_options.exclude[idx_drive][idx_drive_chr++] = optarg[idx_optarg++];
+ }
+ else
+ {
+ /* This section deals with file names that exceed MAX_DRIVE_PATH_LENGTH */
+ nwipe_options.exclude[idx_drive][idx_drive_chr] = 0;
+ while( optarg[idx_optarg] != 0 && optarg[idx_optarg] != ',' )
+ {
+ idx_optarg++;
+ }
+ }
+ }
+ }
+ break;
+
+ case 'h': /* Display help. */
+
+ display_help();
+ break;
+
+ case 'p': /* PRNG option. */
+
+ if( strcmp( optarg, "mersenne" ) == 0 || strcmp( optarg, "twister" ) == 0 )
+ {
+ nwipe_options.prng = &nwipe_twister;
+ break;
+ }
+
+ if( strcmp( optarg, "isaac" ) == 0 )
+ {
+ nwipe_options.prng = &nwipe_isaac;
+ break;
+ }
+
+ if( strcmp( optarg, "isaac64" ) == 0 )
+ {
+ nwipe_options.prng = &nwipe_isaac64;
+ break;
+ }
+
+ /* Else we do not know this PRNG. */
+ fprintf( stderr, "Error: Unknown prng '%s'.\n", optarg );
+ exit( EINVAL );
+
+ case 'q': /* Anonymize serial numbers */
+
+ nwipe_options.quiet = 1;
+ break;
+
+ case 'r': /* Rounds option. */
+
+ if( sscanf( optarg, " %i", &nwipe_options.rounds ) != 1 || nwipe_options.rounds < 1 )
+ {
+ fprintf( stderr, "Error: The rounds argument must be a positive integer.\n" );
+ exit( EINVAL );
+ }
+
+ break;
+
+ case 'v': /* verbose */
+
+ nwipe_options.verbose = 1;
+ break;
+
+ case 'V': /* Version option. */
+
+ printf( "%s version %s\n", program_name, version_string );
+ exit( EXIT_SUCCESS );
+
+ default:
+
+ /* Bogus command line argument. */
+ display_help();
+ exit( EINVAL );
+
+ } /* method */
+
+ } /* command line options */
+
+ /* Return the number of options that were processed. */
+ return optind;
+}
+
+void nwipe_options_log( void )
+{
+ extern nwipe_prng_t nwipe_twister;
+ extern nwipe_prng_t nwipe_isaac;
+ extern nwipe_prng_t nwipe_isaac64;
+
+ /**
+ * Prints a manifest of options to the log.
+ */
+
+ nwipe_log( NWIPE_LOG_NOTICE, "Program options are set as follows..." );
+
+ if( nwipe_options.autonuke )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " autonuke = %i (on)", nwipe_options.autonuke );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " autonuke = %i (off)", nwipe_options.autonuke );
+ }
+
+ if( nwipe_options.autopoweroff )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " autopoweroff = %i (on)", nwipe_options.autopoweroff );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " autopoweroff = %i (off)", nwipe_options.autopoweroff );
+ }
+
+ if( nwipe_options.noblank )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " do not perform a final blank pass" );
+ }
+
+ if( nwipe_options.nowait )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " do not wait for a key before exiting" );
+ }
+
+ if( nwipe_options.nosignals )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " do not allow signals to interrupt a wipe" );
+ }
+
+ if( nwipe_options.nogui )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " do not show GUI interface" );
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE, " banner = %s", banner );
+
+ if( nwipe_options.prng == &nwipe_twister )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " prng = Mersenne Twister" );
+ }
+ else
+ {
+ if( nwipe_options.prng == &nwipe_isaac )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac" );
+ }
+ else
+ {
+ if( nwipe_options.prng == &nwipe_isaac64 )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac64" );
+ }
+ else
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, " prng = Undefined" );
+ }
+ }
+ }
+
+ nwipe_log( NWIPE_LOG_NOTICE, " method = %s", nwipe_method_label( nwipe_options.method ) );
+ nwipe_log( NWIPE_LOG_NOTICE, " quiet = %i", nwipe_options.quiet );
+ nwipe_log( NWIPE_LOG_NOTICE, " rounds = %i", nwipe_options.rounds );
+ nwipe_log( NWIPE_LOG_NOTICE, " sync = %i", nwipe_options.sync );
+
+ switch( nwipe_options.verify )
+ {
+ case NWIPE_VERIFY_NONE:
+ nwipe_log( NWIPE_LOG_NOTICE, " verify = %i (off)", nwipe_options.verify );
+ break;
+
+ case NWIPE_VERIFY_LAST:
+ nwipe_log( NWIPE_LOG_NOTICE, " verify = %i (last pass)", nwipe_options.verify );
+ break;
+
+ case NWIPE_VERIFY_ALL:
+ nwipe_log( NWIPE_LOG_NOTICE, " verify = %i (all passes)", nwipe_options.verify );
+ break;
+
+ default:
+ nwipe_log( NWIPE_LOG_NOTICE, " verify = %i", nwipe_options.verify );
+ break;
+ }
+}
+
+void display_help()
+{
+ /**
+ * displays the help section to STDOUT and exits.
+ */
+
+ printf( "Usage: %s [options] [device1] [device2] ...\n", program_name );
+ printf( "Options:\n" );
+ /* Limit line length to a maximum of 80 characters so it looks good in 80x25 terminals i.e shredos */
+ /* ___12345678901234567890123456789012345678901234567890123456789012345678901234567890< Do not exceed */
+ puts( " -V, --version Prints the version number\n" );
+ puts( " -v, --verbose Prints more messages to the log\n" );
+ puts( " -h, --help Prints this help\n" );
+ puts( " --autonuke If no devices have been specified on the command line," );
+ puts( " starts wiping all devices immediately. If devices have" );
+ puts( " been specified, starts wiping only those specified" );
+ puts( " devices immediately.\n" );
+ puts( " --autopoweroff Power off system on completion of wipe delayed for" );
+ puts( " for one minute. During this one minute delay you can" );
+ puts( " abort the shutdown by typing sudo shutdown -c\n" );
+ printf( " --sync=NUM Will perform a sync after NUM writes (default: %d)\n", DEFAULT_SYNC_RATE );
+ puts( " 0 - fdatasync after the disk is completely written" );
+ puts( " fdatasync errors not detected until completion." );
+ puts( " 0 is not recommended as disk errors may cause" );
+ puts( " nwipe to appear to hang" );
+ puts( " 1 - fdatasync after every write" );
+ puts( " Warning: Lower values will reduce wipe speeds." );
+ puts( " 1000 - fdatasync after 1000 writes etc.\n" );
+ puts( " --verify=TYPE Whether to perform verification of erasure" );
+ puts( " (default: last)" );
+ puts( " off - Do not verify" );
+ puts( " last - Verify after the last pass" );
+ puts( " all - Verify every pass\n" );
+ puts( " -m, --method=METHOD The wiping method. See man page for more details." );
+ puts( " (default: dodshort)" );
+ puts( " dod522022m / dod - 7 pass DOD 5220.22-M method" );
+ puts( " dodshort / dod3pass - 3 pass DOD method" );
+ puts( " gutmann - Peter Gutmann's Algorithm" );
+ puts( " ops2 - RCMP TSSIT OPS-II" );
+ puts( " random / prng / stream - PRNG Stream" );
+ puts( " zero / quick - Overwrite with zeros" );
+ puts( " one - Overwrite with ones (0xFF)" );
+ puts( " verify_zero - Verifies disk is zero filled" );
+ puts( " verify_one - Verifies disk is 0xFF filled\n" );
+ puts( " -l, --logfile=FILE Filename to log to. Default is STDOUT\n" );
+ puts( " -p, --prng=METHOD PRNG option (mersenne|twister|isaac|isaac64)\n" );
+ puts( " -q, --quiet Anonymize logs and the GUI by removing unique data, i.e." );
+ puts( " serial numbers, LU WWN Device ID, and SMBIOS/DMI data" );
+ puts( " XXXXXX = S/N exists, ????? = S/N not obtainable\n" );
+ puts( " -r, --rounds=NUM Number of times to wipe the device using the selected" );
+ puts( " method (default: 1)\n" );
+ puts( " --noblank Do NOT blank disk after wipe" );
+ puts( " (default is to complete a final blank pass)\n" );
+ puts( " --nowait Do NOT wait for a key before exiting" );
+ puts( " (default is to wait)\n" );
+ puts( " --nosignals Do NOT allow signals to interrupt a wipe" );
+ puts( " (default is to allow)\n" );
+ puts( " --nogui Do NOT show the GUI interface. Automatically invokes" );
+ puts( " the nowait option. Must be used with the --autonuke" );
+ puts( " option. Send SIGUSR1 to log current stats\n" );
+ puts( " --nousb Do NOT show or wipe any USB devices whether in GUI" );
+ puts( " mode, --nogui or --autonuke modes.\n" );
+ puts( " -e, --exclude=DEVICES Up to ten comma separated devices to be excluded" );
+ puts( " --exclude=/dev/sdc" );
+ puts( " --exclude=/dev/sdc,/dev/sdd" );
+ puts( " --exclude=/dev/sdc,/dev/sdd,/dev/mapper/cryptswap1\n" );
+ puts( "" );
+ exit( EXIT_SUCCESS );
+}
diff --git a/src/options.h b/src/options.h
new file mode 100644
index 0000000..f70a3ec
--- /dev/null
+++ b/src/options.h
@@ -0,0 +1,72 @@
+/*
+ * options.h: Command line processing routines 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.
+ *
+ */
+
+#ifndef OPTIONS_H_
+#define OPTIONS_H_
+
+/* Program knobs. */
+#define NWIPE_KNOB_ENTROPY "/dev/urandom"
+#define NWIPE_KNOB_IDENTITY_SIZE 512
+#define NWIPE_KNOB_LABEL_SIZE 128
+#define NWIPE_KNOB_LOADAVG "/proc/loadavg"
+#define NWIPE_KNOB_LOG_BUFFERSIZE 1024 // Maximum length of a log event.
+#define NWIPE_KNOB_PARTITIONS "/proc/partitions"
+#define NWIPE_KNOB_PARTITIONS_PREFIX "/dev/"
+#define NWIPE_KNOB_PRNG_STATE_LENGTH 512 // 128 words
+#define NWIPE_KNOB_SCSI "/proc/scsi/scsi"
+#define NWIPE_KNOB_SLEEP 1
+#define NWIPE_KNOB_STAT "/proc/stat"
+#define MAX_NUMBER_EXCLUDED_DRIVES 10
+#define MAX_DRIVE_PATH_LENGTH 200 // e.g. /dev/sda is only 8 characters long, so 200 should be plenty.
+#define DEFAULT_SYNC_RATE 100000
+
+/* Function prototypes for loading options from the environment and command line. */
+int nwipe_options_parse( int argc, char** argv );
+void nwipe_options_log( void );
+
+/* Function to display help text */
+void display_help();
+
+typedef struct
+{
+ int autonuke; // Do not prompt the user for confirmation when set.
+ int autopoweroff; // Power off on completion of wipe
+ int noblank; // Do not perform a final blanking pass.
+ int nousb; // Do not show or wipe any USB devices.
+ int nowait; // Do not wait for a final key before exiting.
+ int nosignals; // Do not allow signals to interrupt a wipe.
+ int nogui; // Do not show the GUI.
+ char* banner; // The product banner shown on the top line of the screen.
+ void* method; // A function pointer to the wipe method that will be used.
+ char logfile[FILENAME_MAX]; // The filename to log the output to.
+ char exclude[MAX_NUMBER_EXCLUDED_DRIVES][MAX_DRIVE_PATH_LENGTH]; // Drives excluded from the search.
+ nwipe_prng_t* prng; // The pseudo random number generator implementation.
+ int quiet; // Anonymize serial numbers
+ int rounds; // The number of times that the wipe method should be called.
+ int sync; // A flag to indicate whether and how often writes should be sync'd.
+ int verbose; // Make log more verbose
+ nwipe_verify_t verify; // A flag to indicate whether writes should be verified.
+} nwipe_options_t;
+
+extern nwipe_options_t nwipe_options;
+
+#endif /* OPTIONS_H_ */
diff --git a/src/pass.c b/src/pass.c
new file mode 100644
index 0000000..3f6487a
--- /dev/null
+++ b/src/pass.c
@@ -0,0 +1,866 @@
+/*
+ * pass.c: Routines that read and write patterns to block devices.
+ *
+ * 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.
+ *
+ */
+
+#define _POSIX_C_SOURCE 200809L
+
+#include <stdint.h>
+#include "nwipe.h"
+#include "context.h"
+#include "method.h"
+#include "prng.h"
+#include "options.h"
+#include "pass.h"
+#include "logging.h"
+#include "gui.h"
+
+int nwipe_random_verify( nwipe_context_t* c )
+{
+ /**
+ * Verifies that a random pass was correctly written to the device.
+ *
+ */
+
+ /* The result holder. */
+ int r;
+
+ /* The IO size. */
+ size_t blocksize;
+
+ /* The result buffer for calls to lseek. */
+ off64_t offset;
+
+ /* The input buffer. */
+ char* b;
+
+ /* The pattern buffer that is used to check the input buffer. */
+ char* d;
+
+ /* The number of bytes remaining in the pass. */
+ u64 z = c->device_size;
+
+ if( c->prng_seed.s == NULL )
+ {
+ nwipe_log( NWIPE_LOG_SANITY, "Null seed pointer." );
+ return -1;
+ }
+
+ if( c->prng_seed.length <= 0 )
+ {
+ nwipe_log( NWIPE_LOG_SANITY, "The entropy length member is %i.", c->prng_seed.length );
+ return -1;
+ }
+
+ /* Create the input buffer. */
+ b = malloc( c->device_stat.st_blksize );
+
+ /* Check the memory allocation. */
+ if( !b )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the input buffer." );
+ return -1;
+ }
+
+ /* Create the pattern buffer */
+ d = malloc( c->device_stat.st_blksize );
+
+ /* Check the memory allocation. */
+ if( !d )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the pattern buffer." );
+ free( b );
+ return -1;
+ }
+
+ /* Reset the file pointer. */
+ offset = lseek( c->device_fd, 0, SEEK_SET );
+
+ /* Reset the pass byte counter. */
+ c->pass_done = 0;
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to reset the '%s' file offset.", c->device_name );
+ free( b );
+ free( d );
+ return -1;
+ }
+
+ if( offset != 0 )
+ {
+ /* This is system insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "lseek() returned a bogus offset on '%s'.", c->device_name );
+ free( b );
+ free( d );
+ return -1;
+ }
+
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ /* FIXME: Is there a better way to handle this? */
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ c->fsyncdata_errors++;
+ }
+
+ /* Reseed the PRNG. */
+ c->prng->init( &c->prng_state, &c->prng_seed );
+
+ while( z > 0 )
+ {
+ if( c->device_stat.st_blksize <= z )
+ {
+ blocksize = c->device_stat.st_blksize;
+ }
+ else
+ {
+ /* This is a seatbelt for buggy drivers and programming errors because */
+ /* the device size should always be an even multiple of its blocksize. */
+ blocksize = z;
+ nwipe_log( NWIPE_LOG_WARNING,
+ "%s: The size of '%s' is not a multiple of its block size %i.",
+ __FUNCTION__,
+ c->device_name,
+ c->device_stat.st_blksize );
+ }
+
+ /* Fill the output buffer with the random pattern. */
+ c->prng->read( &c->prng_state, d, blocksize );
+
+ /* Read the buffer in from the device. */
+ r = read( c->device_fd, b, blocksize );
+
+ /* Check the result. */
+ if( r < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to read from '%s'.", c->device_name );
+ return -1;
+ }
+
+ /* Check for a partial read. */
+ if( r != blocksize )
+ {
+ /* TODO: Handle a partial read. */
+
+ /* The number of bytes that were not read. */
+ int s = blocksize - r;
+
+ nwipe_log(
+ NWIPE_LOG_WARNING, "%s: Partial read from '%s', %i bytes short.", __FUNCTION__, c->device_name, s );
+
+ /* Increment the error count. */
+ c->verify_errors += 1;
+
+ /* Bump the file pointer to the next block. */
+ offset = lseek( c->device_fd, s, SEEK_CUR );
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log(
+ NWIPE_LOG_ERROR, "Unable to bump the '%s' file offset after a partial read.", c->device_name );
+ return -1;
+ }
+
+ } /* partial read */
+
+ /* Compare buffer contents. */
+ if( memcmp( b, d, blocksize ) != 0 )
+ {
+ c->verify_errors += 1;
+ }
+
+ /* Decrement the bytes remaining in this pass. */
+ z -= r;
+
+ /* Increment the total progress counters. */
+ c->pass_done += r;
+ c->round_done += r;
+
+ pthread_testcancel();
+
+ } /* while bytes remaining */
+
+ /* Release the buffers. */
+ free( b );
+ free( d );
+
+ /* We're done. */
+ return 0;
+
+} /* nwipe_random_verify */
+
+int nwipe_random_pass( NWIPE_METHOD_SIGNATURE )
+{
+ /**
+ * Writes a random pattern to the device.
+ *
+ */
+
+ /* The result holder. */
+ int r;
+
+ /* The IO size. */
+ size_t blocksize;
+
+ /* The result buffer for calls to lseek. */
+ off64_t offset;
+
+ /* The output buffer. */
+ char* b;
+
+ /* The number of bytes remaining in the pass. */
+ u64 z = c->device_size;
+
+ /* Number of writes to do before a fdatasync. */
+ int syncRate = nwipe_options.sync;
+
+ /* Counter to track when to do a fdatasync. */
+ int i = 0;
+
+ /* general index counter */
+ int idx;
+
+ if( c->prng_seed.s == NULL )
+ {
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: Null seed pointer." );
+ return -1;
+ }
+
+ if( c->prng_seed.length <= 0 )
+ {
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: The entropy length member is %i.", c->prng_seed.length );
+ return -1;
+ }
+
+ /* Create the initialised output buffer. Initialised because we don't want memory leaks
+ * to disk in the event of some future undetected bug in a prng or its implementation. */
+ b = calloc( c->device_stat.st_blksize, sizeof( char ) );
+
+ /* Check the memory allocation. */
+ if( !b )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the output buffer." );
+ return -1;
+ }
+
+ /* Seed the PRNG. */
+ c->prng->init( &c->prng_state, &c->prng_seed );
+
+ /* Reset the file pointer. */
+ offset = lseek( c->device_fd, 0, SEEK_SET );
+
+ /* Reset the pass byte counter. */
+ c->pass_done = 0;
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to reset the '%s' file offset.", c->device_name );
+ free( b );
+ return -1;
+ }
+
+ if( offset != 0 )
+ {
+ /* This is system insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: lseek() returned a bogus offset on '%s'.", c->device_name );
+ free( b );
+ return -1;
+ }
+
+ while( z > 0 )
+ {
+ if( c->device_stat.st_blksize <= z )
+ {
+ blocksize = c->device_stat.st_blksize;
+ }
+ else
+ {
+ /* This is a seatbelt for buggy drivers and programming errors because */
+ /* the device size should always be an even multiple of its blocksize. */
+ blocksize = z;
+ nwipe_log( NWIPE_LOG_WARNING,
+ "%s: The size of '%s' is not a multiple of its block size %i.",
+ __FUNCTION__,
+ c->device_name,
+ c->device_stat.st_blksize );
+ }
+
+ /* Fill the output buffer with the random pattern. */
+ c->prng->read( &c->prng_state, b, blocksize );
+
+ /* For the first block only, check the prng actually wrote something to the buffer */
+ if( z == c->device_size )
+ {
+ idx = c->device_stat.st_blksize;
+ while( idx > 0 )
+ {
+ if( b[idx] != 0 )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "prng stream is active" );
+ break;
+ }
+ idx--;
+ }
+ if( idx == 0 )
+ {
+ nwipe_log( NWIPE_LOG_FATAL, "ERROR, prng wrote nothing to the buffer" );
+ return -1;
+ }
+ }
+
+ /* Write the next block out to the device. */
+ r = write( c->device_fd, b, blocksize );
+
+ /* Check the result for a fatal error. */
+ if( r < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "write" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to read from '%s'.", c->device_name );
+ return -1;
+ }
+
+ /* Check for a partial write. */
+ if( r != blocksize )
+ {
+ /* TODO: Handle a partial write. */
+
+ /* The number of bytes that were not written. */
+ int s = blocksize - r;
+
+ /* Increment the error count by the number of bytes that were not written. */
+ c->pass_errors += s;
+
+ nwipe_log( NWIPE_LOG_WARNING, "Partial write on '%s', %i bytes short.", c->device_name, s );
+
+ /* Bump the file pointer to the next block. */
+ offset = lseek( c->device_fd, s, SEEK_CUR );
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log(
+ NWIPE_LOG_ERROR, "Unable to bump the '%s' file offset after a partial write.", c->device_name );
+ return -1;
+ }
+
+ } /* partial write */
+
+ /* Decrement the bytes remaining in this pass. */
+ z -= r;
+
+ /* Increment the total progress counters. */
+ c->pass_done += r;
+ c->round_done += r;
+
+ /* Perodic Sync */
+ if( syncRate > 0 )
+ {
+ i++;
+
+ if( i >= syncRate )
+ {
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ nwipe_log( NWIPE_LOG_WARNING, "Wrote %llu bytes on '%s'.", c->pass_done, c->device_name );
+ c->fsyncdata_errors++;
+ free( b );
+ return -1;
+ }
+
+ i = 0;
+ }
+ }
+
+ pthread_testcancel();
+
+ } /* remaining bytes */
+
+ /* Release the output buffer. */
+ free( b );
+
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ /* FIXME: Is there a better way to handle this? */
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ c->fsyncdata_errors++;
+ }
+
+ /* We're done. */
+ return 0;
+
+} /* nwipe_random_pass */
+
+int nwipe_static_verify( NWIPE_METHOD_SIGNATURE, nwipe_pattern_t* pattern )
+{
+ /**
+ * Verifies that a static pass was correctly written to the device.
+ */
+
+ /* The result holder. */
+ int r;
+
+ /* The IO size. */
+ size_t blocksize;
+
+ /* The result buffer for calls to lseek. */
+ off64_t offset;
+
+ /* The input buffer. */
+ char* b;
+
+ /* The pattern buffer that is used to check the input buffer. */
+ char* d;
+
+ /* A pointer into the pattern buffer. */
+ char* q;
+
+ /* The pattern buffer window offset. */
+ int w = 0;
+
+ /* The number of bytes remaining in the pass. */
+ u64 z = c->device_size;
+
+ if( pattern == NULL )
+ {
+ /* Caught insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "nwipe_static_verify: Null entropy pointer." );
+ return -1;
+ }
+
+ if( pattern->length <= 0 )
+ {
+ /* Caught insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "nwipe_static_verify: The pattern length member is %i.", pattern->length );
+ return -1;
+ }
+
+ /* Create the input buffer. */
+ b = malloc( c->device_stat.st_blksize );
+
+ /* Check the memory allocation. */
+ if( !b )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the input buffer." );
+ return -1;
+ }
+
+ /* Create the pattern buffer */
+ d = malloc( c->device_stat.st_blksize + pattern->length * 2 );
+
+ /* Check the memory allocation. */
+ if( !d )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the pattern buffer." );
+ free( b );
+ return -1;
+ }
+
+ for( q = d; q < d + c->device_stat.st_blksize + pattern->length; q += pattern->length )
+ {
+ /* Fill the pattern buffer with the pattern. */
+ memcpy( q, pattern->s, pattern->length );
+ }
+
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ /* FIXME: Is there a better way to handle this? */
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ c->fsyncdata_errors++;
+ }
+
+ /* Reset the file pointer. */
+ offset = lseek( c->device_fd, 0, SEEK_SET );
+
+ /* Reset the pass byte counter. */
+ c->pass_done = 0;
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to reset the '%s' file offset.", c->device_name );
+ free( b );
+ return -1;
+ }
+
+ if( offset != 0 )
+ {
+ /* This is system insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "nwipe_static_verify: lseek() returned a bogus offset on '%s'.", c->device_name );
+ free( b );
+ free( d );
+ return -1;
+ }
+
+ while( z > 0 )
+ {
+ if( c->device_stat.st_blksize <= z )
+ {
+ blocksize = c->device_stat.st_blksize;
+ }
+ else
+ {
+ /* This is a seatbelt for buggy drivers and programming errors because */
+ /* the device size should always be an even multiple of its blocksize. */
+ blocksize = z;
+ nwipe_log( NWIPE_LOG_WARNING,
+ "%s: The size of '%s' is not a multiple of its block size %i.",
+ __FUNCTION__,
+ c->device_name,
+ c->device_stat.st_blksize );
+ }
+
+ /* Fill the output buffer with the random pattern. */
+ /* Read the buffer in from the device. */
+ r = read( c->device_fd, b, blocksize );
+
+ /* Check the result. */
+ if( r < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "read" );
+ nwipe_log( NWIPE_LOG_ERROR, "Unable to read from '%s'.", c->device_name );
+ return -1;
+ }
+
+ /* Check for a partial read. */
+ if( r == blocksize )
+ {
+ /* Check every byte in the buffer. */
+ if( memcmp( b, &d[w], r ) != 0 )
+ {
+ c->verify_errors += 1;
+ }
+ }
+ else
+ {
+ /* The number of bytes that were not read. */
+ int s = blocksize - r;
+
+ /* TODO: Handle a partial read. */
+
+ /* Increment the error count. */
+ c->verify_errors += 1;
+
+ nwipe_log( NWIPE_LOG_WARNING, "Partial read on '%s', %i bytes short.", c->device_name, s );
+
+ /* Bump the file pointer to the next block. */
+ offset = lseek( c->device_fd, s, SEEK_CUR );
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log(
+ NWIPE_LOG_ERROR, "Unable to bump the '%s' file offset after a partial read.", c->device_name );
+ return -1;
+ }
+
+ } /* partial read */
+
+ /* Adjust the window. */
+ w = ( c->device_stat.st_blksize + w ) % pattern->length;
+
+ /* Intuition check:
+ * If the pattern length evenly divides the block size
+ * then ( w == 0 ) always.
+ */
+
+ /* Decrement the bytes remaining in this pass. */
+ z -= r;
+
+ /* Increment the total progress counters. */
+ c->pass_done += r;
+ c->round_done += r;
+
+ pthread_testcancel();
+
+ } /* while bytes remaining */
+
+ /* Release the buffers. */
+ free( b );
+ free( d );
+
+ /* We're done. */
+ return 0;
+
+} /* nwipe_static_verify */
+
+int nwipe_static_pass( NWIPE_METHOD_SIGNATURE, nwipe_pattern_t* pattern )
+{
+ /**
+ * Writes a static pattern to the device.
+ */
+
+ /* The result holder. */
+ int r;
+
+ /* The IO size. */
+ size_t blocksize;
+
+ /* The result buffer for calls to lseek. */
+ off64_t offset;
+
+ /* The output buffer. */
+ char* b;
+
+ /* A pointer into the output buffer. */
+ char* p;
+
+ /* The output buffer window offset. */
+ int w = 0;
+
+ /* The number of bytes remaining in the pass. */
+ u64 z = c->device_size;
+
+ /* Number of writes to do before a fdatasync. */
+ int syncRate = nwipe_options.sync;
+
+ /* Counter to track when to do a fdatasync. */
+ int i = 0;
+
+ if( pattern == NULL )
+ {
+ /* Caught insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: Null pattern pointer." );
+ return -1;
+ }
+
+ if( pattern->length <= 0 )
+ {
+ /* Caught insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: The pattern length member is %i.", pattern->length );
+ return -1;
+ }
+
+ /* Create the output buffer. */
+ b = malloc( c->device_stat.st_blksize + pattern->length * 2 );
+
+ /* Check the memory allocation. */
+ if( !b )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the pattern buffer." );
+ return -1;
+ }
+
+ for( p = b; p < b + c->device_stat.st_blksize + pattern->length; p += pattern->length )
+ {
+ /* Fill the output buffer with the pattern. */
+ memcpy( p, pattern->s, pattern->length );
+ }
+ ///
+ /* Reset the file pointer. */
+ offset = lseek( c->device_fd, 0, SEEK_SET );
+
+ /* Reset the pass byte counter. */
+ c->pass_done = 0;
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to reset the '%s' file offset.", c->device_name );
+ return -1;
+ }
+
+ if( offset != 0 )
+ {
+ /* This is system insanity. */
+ nwipe_log( NWIPE_LOG_SANITY, "__FUNCTION__: lseek() returned a bogus offset on '%s'.", c->device_name );
+ return -1;
+ }
+
+ while( z > 0 )
+ {
+ if( c->device_stat.st_blksize <= z )
+ {
+ blocksize = c->device_stat.st_blksize;
+ }
+ else
+ {
+ /* This is a seatbelt for buggy drivers and programming errors because */
+ /* the device size should always be an even multiple of its blocksize. */
+ blocksize = z;
+ nwipe_log( NWIPE_LOG_WARNING,
+ "%s: The size of '%s' is not a multiple of its block size %i.",
+ __FUNCTION__,
+ c->device_name,
+ c->device_stat.st_blksize );
+ }
+
+ /* Fill the output buffer with the random pattern. */
+ /* Write the next block out to the device. */
+ r = write( c->device_fd, &b[w], blocksize );
+
+ /* Check the result for a fatal error. */
+ if( r < 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "write" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to write to '%s'.", c->device_name );
+ return -1;
+ }
+
+ /* Check for a partial write. */
+ if( r != blocksize )
+ {
+ /* TODO: Handle a partial write. */
+
+ /* The number of bytes that were not written. */
+ int s = blocksize - r;
+
+ /* Increment the error count. */
+ c->pass_errors += s;
+
+ nwipe_log( NWIPE_LOG_WARNING, "Partial write on '%s', %i bytes short.", c->device_name, s );
+
+ /* Bump the file pointer to the next block. */
+ offset = lseek( c->device_fd, s, SEEK_CUR );
+
+ if( offset == (off64_t) -1 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "lseek" );
+ nwipe_log(
+ NWIPE_LOG_ERROR, "Unable to bump the '%s' file offset after a partial write.", c->device_name );
+ return -1;
+ }
+
+ } /* partial write */
+
+ /* Adjust the window. */
+ w = ( c->device_stat.st_blksize + w ) % pattern->length;
+
+ /* Intuition check:
+ *
+ * If the pattern length evenly divides the block size
+ * then ( w == 0 ) always.
+ */
+
+ /* Decrement the bytes remaining in this pass. */
+ z -= r;
+
+ /* Increment the total progress counterr. */
+ c->pass_done += r;
+ c->round_done += r;
+
+ /* Perodic Sync */
+ if( syncRate > 0 )
+ {
+ i++;
+
+ if( i >= syncRate )
+ {
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ nwipe_log( NWIPE_LOG_WARNING, "Wrote %llu bytes on '%s'.", c->pass_done, c->device_name );
+ c->fsyncdata_errors++;
+ free( b );
+ return -1;
+ }
+
+ i = 0;
+ }
+ }
+
+ pthread_testcancel();
+
+ } /* remaining bytes */
+
+ /* Tell our parent that we are syncing the device. */
+ c->sync_status = 1;
+
+ /* Sync the device. */
+ r = fdatasync( c->device_fd );
+
+ /* Tell our parent that we have finished syncing the device. */
+ c->sync_status = 0;
+
+ if( r != 0 )
+ {
+ /* FIXME: Is there a better way to handle this? */
+ nwipe_perror( errno, __FUNCTION__, "fdatasync" );
+ nwipe_log( NWIPE_LOG_WARNING, "Buffer flush failure on '%s'.", c->device_name );
+ c->fsyncdata_errors++;
+ }
+
+ /* Release the output buffer. */
+ free( b );
+
+ /* We're done. */
+ return 0;
+
+} /* nwipe_static_pass */
diff --git a/src/pass.h b/src/pass.h
new file mode 100644
index 0000000..8f27f43
--- /dev/null
+++ b/src/pass.h
@@ -0,0 +1,33 @@
+/*
+ * pass.h: Routines that read and write patterns to block devices.
+ *
+ * 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.
+ *
+ */
+
+#ifndef PASS_H_
+#define PASS_H_
+
+int nwipe_random_pass( nwipe_context_t* c );
+int nwipe_random_verify( nwipe_context_t* c );
+int nwipe_static_pass( nwipe_context_t* c, nwipe_pattern_t* pattern );
+int nwipe_static_verify( nwipe_context_t* c, nwipe_pattern_t* pattern );
+
+void test_functionn( int count, nwipe_context_t** c );
+
+#endif /* PASS_H_ */
diff --git a/src/prng.c b/src/prng.c
new file mode 100644
index 0000000..72c9305
--- /dev/null
+++ b/src/prng.c
@@ -0,0 +1,252 @@
+/*
+ * prng.c: Pseudo Random Number Generator abstractions for nwipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.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.
+ */
+
+#include "nwipe.h"
+#include "prng.h"
+#include "context.h"
+#include "logging.h"
+
+#include "mt19937ar-cok/mt19937ar-cok.h"
+#include "isaac_rand/isaac_rand.h"
+#include "isaac_rand/isaac64.h"
+
+nwipe_prng_t nwipe_twister = { "Mersenne Twister (mt19937ar-cok)", nwipe_twister_init, nwipe_twister_read };
+
+nwipe_prng_t nwipe_isaac = { "ISAAC (rand.c 20010626)", nwipe_isaac_init, nwipe_isaac_read };
+nwipe_prng_t nwipe_isaac64 = { "ISAAC-64 (isaac64.c)", nwipe_isaac64_init, nwipe_isaac64_read };
+
+/* Print given number of bytes from unsigned integer number to a byte stream buffer starting with low-endian. */
+static inline void u32_to_buffer( u8* restrict buffer, u32 val, const int len )
+{
+ for( int i = 0; i < len; ++i )
+ {
+ buffer[i] = ( u8 )( val & 0xFFUL );
+ val >>= 8;
+ }
+}
+static inline void u64_to_buffer( u8* restrict buffer, u64 val, const int len )
+{
+ for( int i = 0; i < len; ++i )
+ {
+ buffer[i] = ( u8 )( val & 0xFFULL );
+ val >>= 8;
+ }
+}
+static inline u32 isaac_nextval( randctx* restrict ctx )
+{
+ if( ctx->randcnt == 0 )
+ {
+ isaac( ctx );
+ ctx->randcnt = RANDSIZ;
+ }
+ ctx->randcnt--;
+ return ctx->randrsl[ctx->randcnt];
+}
+static inline u64 isaac64_nextval( rand64ctx* restrict ctx )
+{
+ if( ctx->randcnt == 0 )
+ {
+ isaac64( ctx );
+ ctx->randcnt = RANDSIZ;
+ }
+ ctx->randcnt--;
+ return ctx->randrsl[ctx->randcnt];
+}
+
+int nwipe_twister_init( NWIPE_PRNG_INIT_SIGNATURE )
+{
+ nwipe_log( NWIPE_LOG_NOTICE, "Initialising Mersenne Twister prng" );
+
+ if( *state == NULL )
+ {
+ /* This is the first time that we have been called. */
+ *state = malloc( sizeof( twister_state_t ) );
+ }
+ twister_init( (twister_state_t*) *state, (u32*) ( seed->s ), seed->length / sizeof( u32 ) );
+ return 0;
+}
+
+int nwipe_twister_read( NWIPE_PRNG_READ_SIGNATURE )
+{
+ u8* restrict bufpos = buffer;
+ size_t words = count / SIZE_OF_TWISTER; // the values of twister_genrand_int32 is strictly 4 bytes
+
+ /* Twister returns 4-bytes per call, so progress by 4 bytes. */
+ for( size_t ii = 0; ii < words; ++ii )
+ {
+ u32_to_buffer( bufpos, twister_genrand_int32( (twister_state_t*) *state ), SIZE_OF_TWISTER );
+ bufpos += SIZE_OF_TWISTER;
+ }
+
+ /* If there is some remainder copy only relevant number of bytes to not
+ * overflow the buffer. */
+ const size_t remain = count % SIZE_OF_TWISTER; // SIZE_OF_TWISTER is strictly 4 bytes
+ if( remain > 0 )
+ {
+ u32_to_buffer( bufpos, twister_genrand_int32( (twister_state_t*) *state ), remain );
+ }
+
+ return 0;
+}
+
+int nwipe_isaac_init( NWIPE_PRNG_INIT_SIGNATURE )
+{
+ int count;
+ randctx* isaac_state = *state;
+
+ nwipe_log( NWIPE_LOG_NOTICE, "Initialising Isaac prng" );
+
+ if( *state == NULL )
+ {
+ /* This is the first time that we have been called. */
+ *state = malloc( sizeof( randctx ) );
+ isaac_state = *state;
+
+ /* Check the memory allocation. */
+ if( isaac_state == 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the isaac state." );
+ return -1;
+ }
+ }
+
+ /* Take the minimum of the isaac seed size and available entropy. */
+ if( sizeof( isaac_state->randrsl ) < seed->length )
+ {
+ count = sizeof( isaac_state->randrsl );
+ }
+ else
+ {
+ memset( isaac_state->randrsl, 0, sizeof( isaac_state->randrsl ) );
+ count = seed->length;
+ }
+
+ if( count == 0 )
+ {
+ /* Start ISACC without a seed. */
+ randinit( isaac_state, 0 );
+ }
+ else
+ {
+ /* Seed the ISAAC state with entropy. */
+ memcpy( isaac_state->randrsl, seed->s, count );
+
+ /* The second parameter indicates that randrsl is non-empty. */
+ randinit( isaac_state, 1 );
+ }
+
+ return 0;
+}
+
+int nwipe_isaac_read( NWIPE_PRNG_READ_SIGNATURE )
+{
+ randctx* isaac_state = *state;
+ u8* restrict bufpos = buffer;
+ size_t words = count / SIZE_OF_ISAAC; // the values of isaac is strictly 4 bytes
+
+ /* Isaac returns 4-bytes per call, so progress by 4 bytes. */
+ for( size_t ii = 0; ii < words; ++ii )
+ {
+ /* get the next 32bit random number */
+ u32_to_buffer( bufpos, isaac_nextval( isaac_state ), SIZE_OF_ISAAC );
+ bufpos += SIZE_OF_ISAAC;
+ }
+
+ /* If there is some remainder copy only relevant number of bytes to not overflow the buffer. */
+ const size_t remain = count % SIZE_OF_ISAAC; // SIZE_OF_ISAAC is strictly 4 bytes
+ if( remain > 0 )
+ {
+ u32_to_buffer( bufpos, isaac_nextval( isaac_state ), remain );
+ }
+
+ return 0;
+}
+
+int nwipe_isaac64_init( NWIPE_PRNG_INIT_SIGNATURE )
+{
+ int count;
+ rand64ctx* isaac_state = *state;
+
+ nwipe_log( NWIPE_LOG_NOTICE, "Initialising ISAAC-64 prng" );
+
+ if( *state == NULL )
+ {
+ /* This is the first time that we have been called. */
+ *state = malloc( sizeof( rand64ctx ) );
+ isaac_state = *state;
+
+ /* Check the memory allocation. */
+ if( isaac_state == 0 )
+ {
+ nwipe_perror( errno, __FUNCTION__, "malloc" );
+ nwipe_log( NWIPE_LOG_FATAL, "Unable to allocate memory for the isaac state." );
+ return -1;
+ }
+ }
+
+ /* Take the minimum of the isaac seed size and available entropy. */
+ if( sizeof( isaac_state->randrsl ) < seed->length )
+ {
+ count = sizeof( isaac_state->randrsl );
+ }
+ else
+ {
+ memset( isaac_state->randrsl, 0, sizeof( isaac_state->randrsl ) );
+ count = seed->length;
+ }
+
+ if( count == 0 )
+ {
+ /* Start ISACC without a seed. */
+ rand64init( isaac_state, 0 );
+ }
+ else
+ {
+ /* Seed the ISAAC state with entropy. */
+ memcpy( isaac_state->randrsl, seed->s, count );
+
+ /* The second parameter indicates that randrsl is non-empty. */
+ rand64init( isaac_state, 1 );
+ }
+
+ return 0;
+}
+
+int nwipe_isaac64_read( NWIPE_PRNG_READ_SIGNATURE )
+{
+ rand64ctx* isaac_state = *state;
+ u8* restrict bufpos = buffer;
+ size_t words = count / SIZE_OF_ISAAC64; // the values of ISAAC-64 is strictly 8 bytes
+
+ for( size_t ii = 0; ii < words; ++ii )
+ {
+ u64_to_buffer( bufpos, isaac64_nextval( isaac_state ), SIZE_OF_ISAAC64 );
+ bufpos += SIZE_OF_ISAAC64;
+ }
+
+ /* If there is some remainder copy only relevant number of bytes to not overflow the buffer. */
+ const size_t remain = count % SIZE_OF_ISAAC64; // SIZE_OF_ISAAC64 is strictly 8 bytes
+ if( remain > 0 )
+ {
+ u64_to_buffer( bufpos, isaac64_nextval( isaac_state ), remain );
+ }
+
+ return 0;
+}
diff --git a/src/prng.h b/src/prng.h
new file mode 100644
index 0000000..8dfa3d7
--- /dev/null
+++ b/src/prng.h
@@ -0,0 +1,65 @@
+/*
+ * prng.h: Pseudo Random Number Generator abstractions for nwipe.
+ *
+ * Copyright Darik Horn <dajhorn-dban@vanadac.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.
+ *
+ */
+
+#ifndef PRNG_H_
+#define PRNG_H_
+
+#include <sys/types.h>
+
+/* A chunk of random data. */
+typedef struct
+{
+ size_t length; // Length of the entropy string in bytes.
+ u8* s; // The actual bytes of the entropy string.
+} nwipe_entropy_t;
+
+#define NWIPE_PRNG_INIT_SIGNATURE void **state, nwipe_entropy_t *seed
+#define NWIPE_PRNG_READ_SIGNATURE void **state, void *buffer, size_t count
+
+/* Function pointers for PRNG actions. */
+typedef int ( *nwipe_prng_init_t )( NWIPE_PRNG_INIT_SIGNATURE );
+typedef int ( *nwipe_prng_read_t )( NWIPE_PRNG_READ_SIGNATURE );
+
+/* The generic PRNG definition. */
+typedef struct
+{
+ const char* label; // The name of the pseudo random number generator.
+ nwipe_prng_init_t init; // Inialize the prng state with the seed.
+ nwipe_prng_read_t read; // Read data from the prng.
+} nwipe_prng_t;
+
+/* Mersenne Twister prototypes. */
+int nwipe_twister_init( NWIPE_PRNG_INIT_SIGNATURE );
+int nwipe_twister_read( NWIPE_PRNG_READ_SIGNATURE );
+
+/* ISAAC prototypes. */
+int nwipe_isaac_init( NWIPE_PRNG_INIT_SIGNATURE );
+int nwipe_isaac_read( NWIPE_PRNG_READ_SIGNATURE );
+int nwipe_isaac64_init( NWIPE_PRNG_INIT_SIGNATURE );
+int nwipe_isaac64_read( NWIPE_PRNG_READ_SIGNATURE );
+
+/* Size of the twister is not derived from the architecture, but it is strictly 4 bytes */
+#define SIZE_OF_TWISTER 4
+
+/* Size of the isaac/isaac64 is not derived from the architecture, but it is strictly 4 or 8 bytes */
+#define SIZE_OF_ISAAC 4
+#define SIZE_OF_ISAAC64 8
+
+#endif /* PRNG_H_ */
diff --git a/src/temperature.c b/src/temperature.c
new file mode 100644
index 0000000..d6511c3
--- /dev/null
+++ b/src/temperature.c
@@ -0,0 +1,270 @@
+/*
+ * temperature.c: functions that populate the drive temperature variables
+ * in each drives context structure.
+ *
+ * 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.
+ */
+
+//#define _LARGEFILE64_SOURCE
+//#define _FILE_OFFSET_BITS 64
+#define _BSD_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <dirent.h>
+
+#include "nwipe.h"
+#include "context.h"
+#include "method.h"
+#include "device.h"
+#include "prng.h"
+#include "options.h"
+#include "device.h"
+#include "logging.h"
+#include "temperature.h"
+
+int nwipe_init_temperature( nwipe_context_t* c )
+{
+ /* This function is called after each nwipe_context_t has been created.
+ * It initialises the temperature variables in each context and then
+ * constructs a path that is placed in the context that points to the
+ * appropriate /sys/class/hwmon/hwmonX directory that corresponds with
+ * the particular drive represented in the context structure.
+ */
+ DIR* dir;
+ DIR* dir2;
+ const char dirpath[] = "/sys/class/hwmon";
+ char dirpath_tmp[256];
+ char dirpath_tmp2[256];
+ char dirpath_hwmonX[256];
+ char device[256];
+ char device_context_name[256];
+ // const char dirpath[] = "/home/nick/mouse/hwmon1";
+ struct dirent* dp;
+ struct dirent* dp2;
+
+ /* Why Initialise with 1000000? Because the GUI needs to know whether data
+ * has been obtained so it can display appropriate information when a
+ * device is unable to provide temperature data */
+
+ c->temp1_crit = 1000000;
+ c->temp1_highest = 1000000;
+ c->temp1_input = 1000000;
+ c->temp1_lcrit = 1000000;
+ c->temp1_lowest = 1000000;
+ c->temp1_max = 1000000;
+ c->temp1_min = 1000000;
+ c->temp1_monitored_wipe_max = 1000000;
+ c->temp1_monitored_wipe_min = 1000000;
+ c->temp1_monitored_wipe_avg = 1000000;
+ c->temp1_flash_rate = 2;
+ c->temp1_flash_rate_counter = 0;
+ c->temp1_path[0] = 0;
+ c->temp1_time = 0;
+
+ /* Each hwmonX directory is processed in turn and once a hwmonX directory has been
+ * found that is a block device and the block device name matches the drive
+ * name in the current context then the path to ../hwmonX is constructed and written
+ * to the drive context structure '* c'. This path is used in the nwipe_update_temperature
+ * function to retrieve temperature data and store it in the device context
+ */
+
+ if( ( dir = opendir( dirpath ) ) != NULL )
+ {
+ /* Process each hwmonX sub directory in turn */
+ while( ( dp = readdir( dir ) ) != NULL )
+ {
+ /* Does the directory start with 'hwmon' */
+ if( strstr( dp->d_name, "hwmon" ) != NULL )
+ {
+ if( nwipe_options.verbose )
+ {
+ /* print a empty line to separate the different hwmon sensors */
+ nwipe_log( NWIPE_LOG_DEBUG, "hwmon:" );
+ }
+ strcpy( dirpath_tmp, dirpath );
+ strcat( dirpath_tmp, "/" );
+ strcat( dirpath_tmp, dp->d_name );
+ strcpy( dirpath_hwmonX, dirpath_tmp );
+ strcat( dirpath_tmp, "/device/block" );
+
+ /* Depending on the class of block device, the device name may
+ * appear in different sub-directories. So we try to open each
+ * directory that are known to contain block devices. These are
+ * /sys/class/hwmon/hwmonX/device/block
+ * /sys/class/hwmon/hwmonX/device/nvme/nvme0
+ * /sys/class/hwmon/hwmonX/device/
+ */
+
+ if( ( dir2 = opendir( dirpath_tmp ) ) == NULL )
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_DEBUG, "hwmon: %s doesn't exist, trying next path", dirpath_tmp );
+ }
+ strcpy( dirpath_tmp2, dirpath_hwmonX );
+ strcat( dirpath_tmp2, "/device/nvme/nvme0" );
+ strcpy( dirpath_tmp, dirpath_tmp2 );
+
+ if( ( dir2 = opendir( dirpath_tmp ) ) == NULL )
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_DEBUG, "hwmon: %s doesn't exist, trying next path", dirpath_tmp );
+ }
+
+ strcpy( dirpath_tmp2, dirpath_hwmonX );
+ strcat( dirpath_tmp2, "/device" );
+ strcpy( dirpath_tmp, dirpath_tmp2 );
+
+ if( ( dir2 = opendir( dirpath_tmp ) ) == NULL )
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log(
+ NWIPE_LOG_DEBUG, "hwmon: %s doesn't exist, no more paths to try", dirpath_tmp );
+ }
+ continue;
+ }
+ }
+ }
+
+ if( dir2 != NULL )
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_DEBUG, "hwmon: Found %s", dirpath_tmp );
+ }
+
+ /* Read the device name */
+ while( ( dp2 = readdir( dir2 ) ) != NULL )
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_DEBUG, "hwmon: dirpath_tmp=%s/%s", dirpath_tmp, &dp2->d_name[0] );
+ }
+
+ /* Skip the '.' and '..' directories */
+ if( dp2->d_name[0] == '.' )
+ {
+ continue;
+ }
+ strcpy( device, dp2->d_name );
+
+ /* Create a copy of the device name from the context but strip the path from it, right justify
+ * device name, prefix with spaces so length is 8. */
+ nwipe_strip_path( device_context_name, c->device_name );
+
+ /* Remove leading/training whitespace from a string and left justify result */
+ trim( device_context_name );
+
+ /* Does the hwmon device match the device for this drive context */
+ if( strcmp( device, device_context_name ) != 0 )
+ {
+ /* No, so try next hwmon device */
+ continue;
+ }
+ else
+ {
+ /* Match ! This hwmon device matches this context, so write the hwmonX path to the context
+ */
+ nwipe_log( NWIPE_LOG_NOTICE, "hwmon: %s has temperature monitoring", device, dirpath_tmp );
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_DEBUG, "hwmon: %s found in %s", device, dirpath_tmp );
+ }
+ /* Copy the hwmon path to the drive context structure */
+ strcpy( c->temp1_path, dirpath_hwmonX );
+ }
+ }
+ closedir( dir2 );
+ }
+ }
+ }
+ closedir( dir );
+ }
+
+ return 0;
+}
+
+void nwipe_update_temperature( nwipe_context_t* c )
+{
+ /* For the given drive context obtain the path to it's hwmon temperature settings
+ * and read then write the temperature values back to the context. A numeric ascii to integer conversion is
+ * performed. The temperaures should be updated no more frequently than every 60 seconds
+ */
+
+ char temperature_label[NUMBER_OF_FILES][20] = {
+ "temp1_crit", "temp1_highest", "temp1_input", "temp1_lcrit", "temp1_lowest", "temp1_max", "temp1_min" };
+ int* temperature_pcontext[NUMBER_OF_FILES] = {
+
+ &( c->temp1_crit ),
+ &( c->temp1_highest ),
+ &( c->temp1_input ),
+ &( c->temp1_lcrit ),
+ &( c->temp1_lowest ),
+ &( c->temp1_max ),
+ &( c->temp1_min ) };
+
+ char path[256];
+ char temperature[256];
+ FILE* fptr;
+ int idx;
+ int result;
+
+ for( idx = 0; idx < NUMBER_OF_FILES; idx++ )
+ {
+ /* Construct the full path including filename */
+ strcpy( path, c->temp1_path );
+ strcat( path, "/" );
+ strcat( path, &( temperature_label[idx][0] ) );
+
+ /* Open the file */
+ if( ( fptr = fopen( path, "r" ) ) != NULL )
+ {
+ /* Acquire data until we reach a newline */
+ result = fscanf( fptr, "%[^\n]", temperature );
+
+ /* Convert numeric ascii to binary integer */
+ *( temperature_pcontext[idx] ) = atoi( temperature );
+
+ /* Divide by 1000 to get degrees celcius */
+ *( temperature_pcontext[idx] ) = *( temperature_pcontext[idx] ) / 1000;
+
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "hwmon: %s %dC", path, *( temperature_pcontext[idx] ) );
+ }
+
+ fclose( fptr );
+ }
+ else
+ {
+ if( nwipe_options.verbose )
+ {
+ nwipe_log( NWIPE_LOG_NOTICE, "hwmon: Unable to open %s", path );
+ }
+ }
+ }
+
+ /* Update the time stamp that records when we checked the temperature,
+ * this is used by the GUI to check temperatures periodically, typically
+ * every 60 seconds */
+ c->temp1_time = time( NULL );
+
+ return;
+}
diff --git a/src/temperature.h b/src/temperature.h
new file mode 100644
index 0000000..d538822
--- /dev/null
+++ b/src/temperature.h
@@ -0,0 +1,30 @@
+/*.
+ * temperature.h: The header file for disk drive temperature sensing
+ *
+ * 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.
+ *
+ */
+
+#ifndef TEMPERATURE_H_
+#define TEMPERATURE_H_
+
+#include <sys/types.h>
+#include "context.h"
+
+int nwipe_init_temperature( nwipe_context_t* );
+void nwipe_update_temperature( nwipe_context_t* );
+
+#define NUMBER_OF_FILES 7
+
+#endif /* TEMPERATURE_H_ */
diff --git a/src/version.c b/src/version.c
new file mode 100644
index 0000000..8abd0af
--- /dev/null
+++ b/src/version.c
@@ -0,0 +1,17 @@
+/**
+ * version_string and program_name are used by siege
+ * and configure; author_name and email_address are
+ * used by configure to dynamically assign those values
+ * to documentation files.
+ */
+const char* version_string = "0.33";
+const char* program_name = "nwipe";
+const char* author_name = "Martijn van Brummelen";
+const char* email_address = "git@brumit.nl";
+const char* years = "2022";
+const char* copyright = "Copyright Darik Horn <dajhorn-dban@vanadac.com>\n\
+Modifications to original dwipe Copyright Andy Beverley <andy@andybev.com>\n\
+This is free software; see the source for copying conditions.\n\
+There is NO warranty; not even for MERCHANTABILITY or FITNESS\n\
+FOR A PARTICULAR PURPOSE.\n";
+const char* banner = "nwipe 0.33";
diff --git a/src/version.h b/src/version.h
new file mode 100644
index 0000000..b1214b2
--- /dev/null
+++ b/src/version.h
@@ -0,0 +1,11 @@
+#ifndef __VERSION_H
+#define __VERSION_H
+
+extern char* version_string;
+extern char* program_name;
+extern char* author_name;
+extern char* email_address;
+extern char* copyright;
+extern char* banner;
+
+#endif /*__VERSION_H*/