NAME

App::USBKeyCopyCon - GUI console for bulk copying of USB keys

SYNOPSIS

To launch the GUI application that this module implements, simply run the supplied wrapper script:

sudo usb-key-copy-con

DESCRIPTION

This module implements an application for bulk copying USB flash drives (storage devices). The application was developed to run on Linux and is probably not particularly portable to other platforms.

From a user's perspective the operation is simple:

  1. insert a 'master' USB key when prompted - the contents of the key will be copied into a temporary directory on the hard drive, after which the key can be removed

  2. insert blank keys into all available USB ports - the app will detect when each new key is inserted, start the copy process and alert the user on completion

  3. repeat step 2 as required

The program can write to multiple keys in parallel. It can also use filtering on device parameters to only overwrite devices which match the vendor name and storage capacity specified - other devices will be ignored.

The specifics of reading the master key, preparing a blank key (formatting parameters etc) are implemented in short 'profile' scripts (a reader and a writer). You can supply your own profile scripts if your requirements differ from those provided.

DEVELOPER INFORMATION

The remainder of the documentation is targetted at developers who wish to modify or customise the application.

The application uses the Gtk2 GUI toolkit. The wrapper script instantiates a single application object like this:

use App::USBKeyCopyCon;

App::USBKeyCopyCon->new->run;

The constructor is responsible for building the user interface and the run method invokes the Gtk2 event loop. UI events are dispatched as method calls on the application object.

ATTRIBUTES

The application object has the following attributes (with correspondingly named accessor methods):

app_win

The main Gtk2::Window object.

capacity_combo

The Gtk2::ComboBox object for the device filter 'Capacity' drop-down menu.

capacity_entry

The Gtk2::Entry object for the device filter 'Capacity' text entry box.

console

The Gtk2::TextView object used for writing output messages.

current_keys

A hash for tracking which (non-master) keys are currently inserted and what stage each copy process is at. The hash key is the device 'UDI' and the value is a hash of device dtails .

current_state

Used to control which mode the application is in:

MASTER-WAIT    waiting for the user to insert the master key
MASTER-COPYING waiting for the master key 'reader' script to complete
MASTER-COPIED  waiting for the user to remove the master key
COPYING        waiting for the user to insert blank keys
exit_status

Used by a SIGCHLD handler to track the exit status of the copy scripts. The key is a process ID and the value is the exist status returned by wait.

hal

The DBus object ('org.freedesktop.Hal.Manager') from which device add/remove events are received.

key_rack

The Gtk2::HBox object containing the widgets representing currently inserted keys.

master_info

A hash of device details for the 'master' USB key.

master_root

The path to the temp directory containing the copy of the master key.

mount_dir

The path to the temp directory containing temporary mount points.

The volume label read from the master key and to be applied to the copies.

reader_script

The path to the profile script used to read the master key.

selected_sound

Pathname of the currently selected sound file, to be played when copying is complete.

temp_root

The temp directory selected by the user. The application will create a subdirectory for the copy of the master key and for temporary mount points.

vendor_combo

The Gtk2::ComboBox object for the device filter 'Vendor' drop-down menu.

vendor_entry

The Gtk2::Entry object for the device filter 'Vendor' text entry box.

volume_label
writer_script

The path to the profile script used to write to the blank keys.

PROFILES

The tasks of reading a master key and writing to a blank key are delegated to 'reader' and 'writer' scripts. A pair of reader/writer scripts is supplied but the application is designed to support using different scripts as dictated by a user selection. The supplied script assume file-by-file copying and format the blank keys with a VFAT filesystem. An alternate profile might use dd to write a complete filesystem in a single operation.

A pair of scripts is referred to as a copying 'profile'. The select_profile method can be used to instruct the application to use a specific pair of scripts.

The supplied scripts are called:

copyfiles-reader.sh
copyfiles-writer.sh

The default constructor selects this profile with the call:

$self->select_profile('copyfiles');

The reader/writer scripts do not have to be shell scripts - they merely need to be executable. The application ignores the file extension if it is present.

METHODS

Constructor

The new method is used to create an application object. It in turn calls BUILD to create and populate the application window and hook into HAL (the Hardware Abstraction Layer) via DBus to get notifications of devices been added/removed.

add_key_to_rack ( key_info )

Called from hal_device_added if the newly added device matches the current device filter settings. The key_info parameter supplied is a hashref of device properties as returned by hal_device_properties. A GUI widget representing the new USB key is added to the user interface and a data structure to track the copying process is created.

build_console ( )

Called from the constructor to create the scrolled text window for displaying progress messages.

build_filters ( )

Called from the constructor to create the toolbar of drop-down menus and text entries for the device filter settings.

build_key_rack ( )

Called from the constructor to create the container widget to house the per-key status indicators.

build_menu ( )

Called from the constructor to create the application menu and hook the menu items up to handler methods.

clean_temp_dir ( )

Called from the run method immediately before the application exits. This method is responsible for removing the temporary directories containing the master copy of the files and the mount points for the blank keys.

confirm_master_dialog ( key_info )

This method is called each time a USB key is inserted when the application is in the MASTER-WAIT state. The key_info parameter supplied is a hashref of device properties as returned by hal_device_properties. this method displays a dialog box to allow the user to confirm that the device should be used as the master key.

If the user selects 'Cancel', no further action is taken and the application goes back to waiting for a master key to be inserted.

If the user confirms the device should be used as the master, then control is passed to the start_master_read method.

copy_finished ( exit_status )

Called when a 'writer' process exits. Checks the exit status and updates the icon in the key rack (0 = success, non-zero = failure).

disable_filter_inputs ( )

This method is called from require_master_key to disable the menu and text entry widgets on the device filter toolbar.

enable_filter_inputs ( )

This method is called from require_master_key to enable the menu and text entry widgets on the device filter toolbar.

fork_copier ( key_info )

Called from add_key_to_rack. Forks a 'writer' process and collects its STDOUT+STDERR via a pipe.

get_volume_label ( device )

Called from confirm_master_dialog when collecting information about the key which was just inserted. Current implementation simply runs the dosfslabel command.

hal_device_added ( udi )

Called to handle a 'DeviceAdded' event from HAL via DBus. Delegates to start_master_read if the app is waiting for a master key. Otherwise checks whether the new device parameters match the current filter settings and delegates to add_key_to_rack if they do.

hal_device_properties ( udi )

Called from hal_device_added to query HAL. Returns a hash(ref) of device details. The global variable %hal_device_added defines which attributes returned from HAL will appear in the hash and which keys they will be mapped to.

hal_device_removed ( udi )

Called to handle a 'DeviceRemoved' event from HAL via DBus. Delegates to remove_key_from_rack if the application is in the COPYING state.

init_dbus_watcher ( )

Called from the constructor to hook up device-add events to the hal_device_added method and device-remove events to hal_device_removed.

master_copy_finished ( exit_status )

Called when the 'reader' process exits. Checks the exit status and updates the application state to <MASTER-COPIED> on success or MASTER-WAIT on failure.

match_device_filter ( key_info )

Called from hal_device_added and returns true if the device matches the current filter parameters, or false otherwise.

on_copier_pipe_read ( fileno, condition, udi )

Handler for data received from a 'writer' process. Updates the status icon for the device to indicate progress.

on_master_pipe_read ( fileno, condition, udi )

Handler for data received from the master key 'reader' process. Copies output from the process to the console widget.

on_menu_edit_preferences ( )

Handler for the Edit > Preferences menu item - not currently implemented.

on_menu_file_new ( )

Handler for the File > New menu item. Resets the application state via require_master_key.

on_menu_file_quit ( )

Handler for the File > Quit menu item. Exits the Gtk event loop, which returns control to the run method.

on_menu_help_about ( )

Handler for the Help > About menu item. Displays 'About' dialog.

play_sound_file ( sound_file )

This method takes a pathname to a sound file (e.g.: a .wav) and plays it. The current implementation simply runs the the SOX play command - it should probably use GStreamer

remove_key_from_rack ( udi )

Called from hal_device_removed to remove the indicator widget corresponding to the USB key which has just been removed.

require_master_key ( )

Called from the constructor to put the app in the MASTER-WAIT mode (waiting for the master key to be inserted). Can also be called from the on_menu_file_new menu event handler.

run ( )

This method is called from the wrapper script. It's job is to run the Gtk event loop and when that exits, to call clean_temp_dir and then return.

say ( message )

Appends a message to the console widget. (Note, the caller is responsible for supplying the newline characters).

select_profile ( profile_name )

This method is used to select which reader/writer scripts will be used. At present there is one hard-coded call to this method in the constructor. Ideally, the user would select from all available profile scripts in the 'confirm master' dialog.

set_temp_root ( pathname )

Called from confirm_master_dialog based on the temp directory selected by the user.

start_master_read ( key_info )

Called from hal_device_added to fork off a 'reader' process to slurp in the contents of the master key.

tick ( )

This timer event handler is used to take the child process exit status values collected by the SIGCHLD handler and pass them to master_copy_finished or copy_finished as appropriate.

update_key_progress ( udi, status )

Called from on_copier_pipe_read to update the status icon for a specified USB key device. The progress parameter is a number in the range 0-10 for copies in progress; -1 for a copy that has failed (non-zero exit status from the 'writer' process); or -2 to indicate a device which did not match the filter settings and is being ignored.

AUTHOR

Grant McLean, <grantm at cpan.org>

BUGS

Please report any bugs or feature requests to bug-app-usbkeycopycon at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=App-USBKeyCopyCon. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

perldoc App::USBKeyCopyCon

You can also look for information at:

COPYRIGHT & LICENSE

Copyright 2009 Grant McLean, all rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.