- Added a proper Makefile.PL to SCST::SCST so now we have a also a
authormgandalf <mgandalf@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Wed, 9 Jul 2008 17:32:42 +0000 (17:32 +0000)
committermgandalf <mgandalf@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Wed, 9 Jul 2008 17:32:42 +0000 (17:32 +0000)
  proper man page.
- Renamed Changes to ChangeLog
- Updated README to include instructions for installing the perl module.
- Renamed SCST perl module directory tp scst-0.7.4

git-svn-id: https://scst.svn.sourceforge.net/svnroot/scst/trunk@447 d57e44dd-8a1f-0410-8b47-8ef2f437770f

scstadmin/ChangeLog [moved from scstadmin/Changes with 86% similarity]
scstadmin/Makefile
scstadmin/README
scstadmin/scst-0.7.4/MANIFEST [new file with mode: 0644]
scstadmin/scst-0.7.4/Makefile.PL [new file with mode: 0644]
scstadmin/scst-0.7.4/README [new file with mode: 0644]
scstadmin/scst-0.7.4/SCST.pm [moved from scstadmin/SCST/SCST.pm with 100% similarity]
scstadmin/scst-0.7.4/lib/SCST/SCST.pm [new file with mode: 0644]

similarity index 86%
rename from scstadmin/Changes
rename to scstadmin/ChangeLog
index 1781b93..a231318 100644 (file)
@@ -1,3 +1,11 @@
+Changes since 1.0.0
+
+- Added a proper Makefile.PL to SCST::SCST so now we have a also a
+  proper man page.
+- Renamed Changes to ChangeLog
+- Updated README to include instructions for installing the perl module.
+- Renamed SCST perl module directory tp scst-0.7.4
+
 Changes since 1.0.0 RC1
 
 - Updated SCST/SCST.pm to add one more option mapping NV -> NV_CACHE.
 Changes since 1.0.0 RC1
 
 - Updated SCST/SCST.pm to add one more option mapping NV -> NV_CACHE.
index 02c3550..3139cc0 100644 (file)
@@ -1,14 +1,28 @@
-all:
+VERSION = 0.7.4
+TOOL = scstadmin
+
+all: perl-module
 
 install:
 
 install:
+       install -m 755 $(TOOL) /usr/local/sbin
        install -m 755 init.d/scst /etc/init.d
        install -m 755 init.d/scst /etc/init.d
-       install -m 755 scstadmin /usr/local/sbin
-       mkdir -p /usr/local/lib/site_perl/SCST
-       install -m 644 SCST/SCST.pm /usr/local/lib/site_perl/SCST/SCST.pm
+       $(MAKE) -C scst-$(VERSION) install
 
 uninstall:
        -rm -f /etc/init.d/scst
 
 uninstall:
        -rm -f /etc/init.d/scst
-       -rm -f /usr/local/sbin/scstadmin
-       -rm -rf /usr/local/lib/site_perl/SCST
+       -rm -f /usr/local/sbin/$(TOOL)
+       $(MAKE) -C scst-$(VERSION) uninstall
+
+perl-module:
+       @cd ./scst-$(VERSION); \
+       perl Makefile.PL;
+       $(MAKE) -C scst-$(VERSION)
+
+clean:
+       $(MAKE) -C scst-$(VERSION) clean
+
+distclean:
+       $(MAKE) -C scst-$(VERSION) clean
+       -rm -f scst-$(VERSION)/Makefile.old
 
 .PHONY: all install uninstall
 
 .PHONY: all install uninstall
index 81b1787..fe09fc3 100644 (file)
@@ -7,9 +7,7 @@ The perl module SCST::SCST is very generic and tries to handle error checking as
 
 The init script was written for debian but should work on most distributions.
 
 
 The init script was written for debian but should work on most distributions.
 
-       SCST            This is the SCST Perl module required by scstadmin and scst_db.
-                       Copy the SCST directory to your perl5 lib directory:
-                               #> cp -r SCST /usr/lib/perl5/
+       scst-0.7.4      This is the SCST Perl module required by scstadmin and scst_db.
 
        scstadmin       Script which can accept operations on a command line or from
                        a configuration file. See scst.conf. For command help,
 
        scstadmin       Script which can accept operations on a command line or from
                        a configuration file. See scst.conf. For command help,
@@ -28,7 +26,19 @@ The init script was written for debian but should work on most distributions.
        init.d/scst     Init script to start SCST on boot which uses scstadmin.
 
 
        init.d/scst     Init script to start SCST on boot which uses scstadmin.
 
 
+Installation:
+=============
+
+Installation is simple.
+
+  #> make
+  #> make install
+
+This installs scstadmin, the init.d script and the perl module.
+
+
 Getting Started:
 Getting Started:
+================
 
 The scstadmin script is much more functional than scst_db at this point but uses a
 standard text-based config file. The original thought behind scst_db was to write
 
 The scstadmin script is much more functional than scst_db at this point but uses a
 standard text-based config file. The original thought behind scst_db was to write
@@ -38,13 +48,13 @@ client app would then connect to that port.
 Copy scst.conf to /etc and edit it to your liking. if you have an existing configuration
 then have scstadmin write it out to a config file for you:
 
 Copy scst.conf to /etc and edit it to your liking. if you have an existing configuration
 then have scstadmin write it out to a config file for you:
 
-#> scstadmin -WriteConfig /etc/scst.conf
+  #> scstadmin -WriteConfig /etc/scst.conf
 
 When removing devices, users or groups from the config file keep in mind that
 "scstadmin -config" will NOT remove those configurations from the running system unless
 you use the -ForceConfig flag. Also, using the init script to reload the configuration
 
 
 When removing devices, users or groups from the config file keep in mind that
 "scstadmin -config" will NOT remove those configurations from the running system unless
 you use the -ForceConfig flag. Also, using the init script to reload the configuration
 
-#> /etc/init.d/scst reload-config
+  #> /etc/init.d/scst reload-config
 
 will also not remove configurations from a running system.
 
 
 will also not remove configurations from a running system.
 
diff --git a/scstadmin/scst-0.7.4/MANIFEST b/scstadmin/scst-0.7.4/MANIFEST
new file mode 100644 (file)
index 0000000..267f842
--- /dev/null
@@ -0,0 +1,4 @@
+lib/SCST/SCST.pm
+Makefile.PL
+MANIFEST                       This list of files
+README
diff --git a/scstadmin/scst-0.7.4/Makefile.PL b/scstadmin/scst-0.7.4/Makefile.PL
new file mode 100644 (file)
index 0000000..5b400f8
--- /dev/null
@@ -0,0 +1,13 @@
+#!/usr/bin/perl -w
+#
+# Makefile for the SCST-SCST perl module
+#
+
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+        'NAME'          => 'SCST-SCST',
+        'VERSION_FROM'  => 'lib/SCST/SCST.pm',
+        'AUTHOR'        => 'Mark R. Buechler <Mark.Buechler@gmail.com>',
+        'PREREQ_PM'     => {},
+);
diff --git a/scstadmin/scst-0.7.4/README b/scstadmin/scst-0.7.4/README
new file mode 100644 (file)
index 0000000..95df188
--- /dev/null
@@ -0,0 +1,16 @@
+SCST-SCST is a perl module for querying and configuring SCST.
+
+
+AVAILABILITY
+
+Currently SCST-SCST ships with SCST itself available from
+http://scst.sourceforge.net.
+
+
+INSTALLATION
+
+Install SCST-SCST as you would any other perl module:
+
+   perl Makefile.PL
+   make
+   make install
diff --git a/scstadmin/scst-0.7.4/lib/SCST/SCST.pm b/scstadmin/scst-0.7.4/lib/SCST/SCST.pm
new file mode 100644 (file)
index 0000000..6f4d106
--- /dev/null
@@ -0,0 +1,1300 @@
+package SCST::SCST;
+
+# Author:      Mark R. Buechler
+# License:     GPLv2
+# Copyright (c) 2005-2007 Mark R. Buechler
+
+use 5.005;
+use IO::Handle;
+use IO::File;
+use strict;
+use Carp;
+
+my $TRUE  = 1;
+my $FALSE = 0;
+
+my $_SCST_DIR_           = '/proc/scsi_tgt';
+my $_SCST_IO_            = $_SCST_DIR_.'/scsi_tgt';
+my $_SCST_CDROM_IO_      = $_SCST_DIR_.'/dev_cdrom/dev_cdrom';
+my $_SCST_CHANGER_IO_    = $_SCST_DIR_.'/dev_changer/dev_changer';
+my $_SCST_DISK_IO_       = $_SCST_DIR_.'/dev_disk/dev_disk';
+my $_SCST_DISKP_IO_      = $_SCST_DIR_.'/dev_disk_perf/dev_disk_perf';
+my $_SCST_MODISK_IO_     = $_SCST_DIR_.'/dev_modisk/dev_modisk';
+my $_SCST_MODISKP_IO_    = $_SCST_DIR_.'/dev_modisk_perf/dev_modisk_perf';
+my $_SCST_TAPE_IO_       = $_SCST_DIR_.'/dev_tape/dev_tape';
+my $_SCST_TAPEP_IO_      = $_SCST_DIR_.'/dev_tape_perf/dev_tape_perf';
+my $_SCST_VDISK_IO_      = $_SCST_DIR_.'/vdisk/vdisk';
+my $_SCST_VCDROM_IO_     = $_SCST_DIR_.'/vcdrom/vcdrom';
+my $_SCST_PROCESSOR_IO_  = $_SCST_DIR_.'/dev_processor/dev_processor';
+my $_SCST_GROUPS_DIR_    = $_SCST_DIR_.'/groups';
+my $_SCST_SGV_STATS_     = $_SCST_DIR_.'/sgv';
+my $_SCST_SESSIONS_      = $_SCST_DIR_.'/sessions';
+my $_SCST_VERSION_IO_    = $_SCST_DIR_.'/version';
+
+my $_SCST_USERS_IO_      = 'names';
+my $_SCST_DEVICES_IO_    = 'devices';
+
+use vars qw(@ISA @EXPORT $VERSION $CDROM_TYPE $CHANGER_TYPE $DISK_TYPE $VDISK_TYPE
+            $VCDROM_TYPE $DISKPERF_TYPE $MODISK_TYPE $MODISKPERF_TYPE $TAPE_TYPE
+            $TAPEPERF_TYPE $PROCESSOR_TYPE $IOTYPE_PHYSICAL $IOTYPE_VIRTUAL
+            $IOTYPE_PERFORMANCE);
+
+$CDROM_TYPE         = 1;
+$CHANGER_TYPE       = 2;
+$DISK_TYPE          = 3;
+$VDISK_TYPE         = 4;
+$VCDROM_TYPE        = 5;
+$DISKPERF_TYPE      = 6;
+$MODISK_TYPE        = 7;
+$MODISKPERF_TYPE    = 8;
+$TAPE_TYPE          = 9;
+$TAPEPERF_TYPE      = 10;
+$PROCESSOR_TYPE     = 11;
+
+$IOTYPE_PHYSICAL    = 100;
+$IOTYPE_VIRTUAL     = 101;
+$IOTYPE_PERFORMANCE = 102;
+
+$VERSION = 0.7.4;
+
+my $_SCST_MIN_MAJOR_   = 0;
+my $_SCST_MIN_MINOR_   = 9;
+my $_SCST_MIN_RELEASE_ = 6;
+
+my %_IO_MAP_ = ($CDROM_TYPE => $_SCST_CDROM_IO_,
+               $CHANGER_TYPE => $_SCST_CHANGER_IO_,
+               $DISK_TYPE => $_SCST_DISK_IO_,
+               $VDISK_TYPE => $_SCST_VDISK_IO_,
+               $VCDROM_TYPE => $_SCST_VCDROM_IO_,
+               $DISKPERF_TYPE => $_SCST_DISKP_IO_,
+               $MODISK_TYPE => $_SCST_MODISK_IO_,
+               $MODISKPERF_TYPE => $_SCST_MODISKP_IO_,
+               $TAPE_TYPE => $_SCST_TAPE_IO_,
+               $TAPEPERF_TYPE => $_SCST_TAPEP_IO_,
+               $PROCESSOR_TYPE => $_SCST_PROCESSOR_IO_);
+
+my %_TYPE_MAP_ = ('dev_cdrom' => $CDROM_TYPE,
+                 'dev_changer' => $CHANGER_TYPE,
+                 'dev_disk' => $DISK_TYPE,
+                 'vdisk' => $VDISK_TYPE,
+                 'vcdrom' => $VCDROM_TYPE,
+                 'dev_disk_perf' => $DISKPERF_TYPE,
+                 'dev_modisk' => $MODISK_TYPE,
+                 'dev_modisk_perf' => $MODISKPERF_TYPE,
+                 'dev_tape' => $TAPE_TYPE,
+                 'dev_tape_perf' => $TAPEPERF_TYPE,
+                 'dev_processor' => $PROCESSOR_TYPE);
+
+my %_REVERSE_MAP_ = ($CDROM_TYPE => 'dev_cdrom',
+                    $CHANGER_TYPE => 'dev_changer',
+                    $DISK_TYPE => 'dev_disk',
+                    $VDISK_TYPE => 'vdisk',
+                    $VCDROM_TYPE => 'vcdrom',
+                    $DISKPERF_TYPE => 'dev_disk_perf',
+                    $MODISK_TYPE => 'dev_modisk',
+                    $MODISKPERF_TYPE => 'dev_modisk_perf',
+                    $TAPE_TYPE => 'dev_tape',
+                    $TAPEPERF_TYPE => 'dev_tape_perf',
+                    $PROCESSOR_TYPE => 'dev_processor');
+
+my %_IO_TYPES_ = ($CDROM_TYPE => $IOTYPE_PHYSICAL,
+                 $CHANGER_TYPE => $IOTYPE_PHYSICAL,
+                 $DISK_TYPE => $IOTYPE_PHYSICAL,
+                 $VDISK_TYPE => $IOTYPE_VIRTUAL,
+                 $VCDROM_TYPE => $IOTYPE_VIRTUAL,
+                 $DISKPERF_TYPE => $IOTYPE_PERFORMANCE,
+                 $MODISK_TYPE => $IOTYPE_PHYSICAL,
+                 $MODISKPERF_TYPE => $IOTYPE_PERFORMANCE,
+                 $TAPE_TYPE => $IOTYPE_PHYSICAL,
+                 $TAPEPERF_TYPE => $IOTYPE_PERFORMANCE,
+                 $PROCESSOR_TYPE => $IOTYPE_PHYSICAL);
+
+my %_HANDLER_ALIASES_ = ('vdisk_blk' => 'vdisk');
+
+my %_AVAILABLE_OPTIONS_ = ('WRITE_THROUGH' => 'WRITE_THROUGH',
+                          'O_DIRECT'      => 'O_DIRECT',
+                          'READ_ONLY'     => 'READ_ONLY',
+                          'NULLIO'        => 'NULLIO',
+                          'NV_CACHE'      => 'NV_CACHE',
+                          'NV'            => 'NV_CACHE',
+                          'BLOCKIO'       => 'BLOCKIO',
+                          'BIO'           => 'BLOCKIO');
+
+sub new {
+       my $this = shift;
+       my $debug = shift;
+       my $badVersion = $TRUE;
+       
+       my $class = ref($this) || $this;
+       my $self = {};
+
+       bless($self, $class);
+
+       $self->{'debug'} = $debug;
+
+       my $scstVersion = $self->scstVersion();
+
+       my($major, $minor, $release) = split(/\./, $scstVersion, 3);
+       ($release, undef) = split(/\-/, $release) if ($release =~ /\-/);
+
+       $badVersion = $FALSE if (($major > $_SCST_MIN_MAJOR_) ||
+                                (($major == $_SCST_MIN_MAJOR_) && ($minor > $_SCST_MIN_MINOR_)) ||
+                                (($major == $_SCST_MIN_MAJOR_) && ($minor == $_SCST_MIN_MINOR_) && ($release >= $_SCST_MIN_RELEASE_)));
+
+       croak("This module requires at least SCST version $_SCST_MIN_MAJOR_\.$_SCST_MIN_MINOR_\.".
+             "$_SCST_MIN_RELEASE_ and version $scstVersion was found") if ($badVersion);
+
+       return $self;
+}
+
+sub scstVersion {
+       my $self = shift;
+
+       my $io = new IO::File $_SCST_VERSION_IO_, O_RDONLY;
+       return $TRUE if (!$io);
+
+       my $version = <$io>;
+       chomp $version;
+
+       return $version;
+}
+
+sub groups {
+       my $self = shift;
+       my @groups;
+       my $dirHandle = new IO::Handle;
+
+       opendir $dirHandle, $_SCST_GROUPS_DIR_ or return undef;
+      
+       foreach my $entry (readdir($dirHandle)) {
+               next if (($entry eq '.') || ($entry eq '..'));
+
+               push @groups, $entry;
+       }
+
+       close $dirHandle;
+
+       return \@groups;
+}
+
+sub groupExists {
+       my $self = shift;
+       my $group = shift;
+       my $groups = $self->groups();
+
+       foreach my $_group (@{$groups}) {
+               return $TRUE if ($group eq $_group);
+       }
+
+       return $FALSE;
+}
+
+sub addGroup {
+       my $self = shift;
+       my $group = shift;
+
+       return 2 if ($self->groupExists($group));
+
+       my $io = new IO::File $_SCST_IO_, O_WRONLY;
+
+       if (!$io) {
+               $self->{'error'} = "addGroup(): Failed to open handler IO $_SCST_IO_";
+               return $TRUE;
+       }
+
+       my $cmd = "add_group $group\n";
+
+       if ($self->{'debug'}) {
+               print "DBG($$): $_SCST_IO_ -> $cmd\n";
+       } else {
+               print $io $cmd;
+       }
+
+       close $io;
+
+       return $FALSE if ($self->{'debug'});
+       return !$self->groupExists($group);
+}
+
+sub removeGroup {
+       my $self = shift;
+       my $group = shift;
+
+       return 2 if (!$self->groupExists($group));
+
+       my $io = new IO::File $_SCST_IO_, O_WRONLY;
+
+       if (!$io) {
+               $self->{'error'} = "removeGroup(): Failed to open handler IO $_SCST_IO_";
+               return $TRUE;
+       }
+
+       my $cmd = "del_group $group\n";
+
+       if ($self->{'debug'}) {
+               print "DBG($$): $_SCST_IO_ -> $cmd\n";
+       } else {
+               print $io $cmd;
+       }
+
+       close $io;
+
+       return $FALSE if ($self->{'debug'});
+       return $self->groupExists($group);
+}
+
+sub sgvStats {
+       my $self = shift;
+       my $io = new IO::File $_SCST_SGV_STATS_, O_RDONLY;
+       my %stats;
+       my $first = $TRUE;
+
+       if (!$io) {
+               $self->{'error'} = "sgvStats(): Failed to open handler IO $_SCST_IO_";
+               return undef;
+       }
+
+       while (my $line = <$io>) {
+               chomp $line;
+
+               if ($first || !$line) {
+                       $first = $FALSE;
+                       next;
+               }
+
+               my $size;
+               my $stat;
+               my $hit;
+               my $total;
+
+               if ($line !~ /^\s/) {
+                       ($stat, $hit, $total) = split(/\s+/, $line);
+
+                       $size = 'ALL';
+                       if ($stat eq 'big') {
+                               $total = $hit;
+                               $hit = -1;
+                       }
+               } else {
+                       (undef, $stat, $hit, $total) = split(/\s+/, $line);
+
+                       if ($stat =~ /(\d+)K$/) {
+                               $size = $1;
+                               $stat =~ s/\-$size\K//;
+                       }
+               }
+
+               $stats{$stat}->{$size}->{'HITS'} = $hit;
+               $stats{$stat}->{$size}->{'TOTAL'} = $total;
+       }
+
+       close $io;
+
+       return \%stats;
+}
+
+sub sessions {
+       my $self = shift;
+       my $io = new IO::File $_SCST_SESSIONS_, O_RDONLY;
+       my %sessions;
+       my $first = $TRUE;
+
+       if (!$io) {
+               $self->{'error'} = "sessions(): Failed to open handler IO $_SCST_IO_";
+               return undef;
+       }
+
+       while (my $line = <$io>) {
+               chomp $line;
+
+               if ($first) {
+                       $first = $FALSE;
+                       next;
+               }
+
+               my($target, $user, $group, $commands) = split(/\s+/, $line);
+
+               $sessions{$target}->{$group}->{$user} = $commands;
+       }
+
+       close $io;
+
+       return \%sessions;
+}
+
+sub devices {
+       my $self = shift;
+       my $io = new IO::File $_SCST_IO_, O_RDONLY;
+       my %devices;
+       my $first = $TRUE;
+
+       if (!$io) {
+               $self->{'error'} = "devices(): Failed to open handler IO $_SCST_IO_";
+               return undef;
+       }
+
+       while (my $line = <$io>) {
+               chomp $line;
+
+               if ($first) {
+                       $first = $FALSE;
+                       next;
+               }
+
+               my($vname, $handler) = split(/\s+/, $line);
+
+               $handler = $_HANDLER_ALIASES_{$handler} if ($_HANDLER_ALIASES_{$handler});
+               $devices{$vname} = $_TYPE_MAP_{$handler};
+       }
+
+       close $io;
+
+       return \%devices;
+}
+
+sub handlerDevices {
+       my $self = shift;
+       my $handler = shift;
+       my $handler_io = $_IO_MAP_{$handler};
+       my $first = $TRUE;
+       my %devices;
+
+       if (!$handler_io) {
+               $self->{'error'} = "handlerDevices(): Failed to open handler IO $handler_io or handler $handler invalid";
+               return undef;
+       }
+
+       if (!$self->handlerExists($handler)) {
+               $self->{'error'} = "handlerDevices(): Handler $handler does not exist";
+               return undef;
+       }
+
+       my $io = new IO::File $handler_io, O_RDONLY;
+
+       if (!$io) {
+               print "WARNING: handlerDevices(): Failed to open handler IO $handler_io, assuming disabled.\n";
+               return \%devices; # Return an empty hash
+       }
+
+       while (my $line = <$io>) {
+               chomp $line;
+
+               if ($first) {
+                       $first = $FALSE;
+                       next;
+               }
+
+               my ($vname, $size, $blocksize, $options, $path) = split(/\s+/, $line);
+
+               if ($options =~ /^\//) {
+                       $path = $options;
+                       $options = "";
+               }
+
+               $devices{$vname}->{'OPTIONS'} = cleanupString($options);
+               $devices{$vname}->{'SIZE'} = cleanupString($size);
+               $devices{$vname}->{'PATH'} = cleanupString($path);
+               $devices{$vname}->{'BLOCKSIZE'} = cleanupString($blocksize);
+       }
+
+       close $io;
+
+       return \%devices;
+}
+
+sub handlerDeviceExists {
+       my $self = shift;
+       my $handler = shift;
+       my $device = shift;
+       my $devices = $self->handlerDevices($handler);
+
+       return -1 if (!defined($devices));
+       return $TRUE if (defined($$devices{$device}));
+
+       return $FALSE;
+}
+
+sub handlerType {
+       my $self = shift;
+       my $handler = shift;
+
+       my $type = $_IO_TYPES_{$handler};
+
+       if (!$type) {
+               $self->{'error'} = "handlerType(): Handler type for handler $handler not defined";
+               return undef;
+       }
+
+       return $type;
+}
+
+sub openDevice {
+       my $self = shift;
+       my $handler = shift;
+       my $device = shift;
+       my $path = shift;
+       my $options = shift;
+       my $blocksize = shift;
+       my $handler_io = $_IO_MAP_{$handler};
+       my $valid_opts;
+
+       ($options, $valid_opts) = $self->checkOptions($options);
+
+       if (!$valid_opts) {
+               $self->{'error'} = "openDevice(): Invalid option(s) '$options' given for device $device";
+               return $TRUE;
+       }
+
+       if (!$handler_io) {
+               $self->{'error'} = "openDevice(): Failed to open handler IO $handler_io or ".
+                 "handler $handler invalid";
+               return $TRUE;
+       }
+
+       if (!$self->handlerExists($handler)) {
+               $self->{'error'} = "openDevice(): Handler $handler does not exist";
+               return $TRUE;
+       }
+
+       if ($self->handlerDeviceExists($handler, $device)) {
+               $self->{'error'} = "openDevice(): Device $device is already open";
+               return 2;
+       }
+
+       $options = cleanupString($options);
+
+       my $cmd = "open $device $path $blocksize $options\n";
+
+       $cmd = cleanupString($cmd);
+
+       my $rc = $self->handler_private($handler_io, $cmd);
+
+       return $FALSE if ($self->{'debug'});
+       return $rc if ($rc);
+
+       $rc = !$self->handlerDeviceExists($handler, $device);
+
+       if ($rc) {
+               $self->{'error'} = "openDevice(): An error occured while opening device $device. ".
+                 "See dmesg/kernel log for more information.";
+       }
+
+       return $rc;
+}
+
+sub closeDevice {
+       my $self = shift;
+       my $handler = shift;
+       my $device = shift;
+       my $path = shift;
+       my $handler_io = $_IO_MAP_{$handler};
+
+       if (!$handler_io) {
+               $self->{'error'} = "closeDevice(): Failed to open handler IO $handler_io or handler $handler invalid";
+               return $TRUE;
+       }
+
+       if (!$self->handlerExists($handler)) {
+               $self->{'error'} = "closeDevice(): Handler $handler does not exist";
+               return $TRUE;
+       }
+
+       if (!$self->handlerDeviceExists($handler, $device)) {
+               $self->{'error'} = "closeDevice(): Device $device is not open";
+               return 2;
+       }
+
+       my $cmd = "close $device $path\n";
+
+       my $rc = $self->handler_private($handler_io, $cmd);
+
+       return $FALSE if ($self->{'debug'});
+       return $rc if ($rc);
+
+       $rc = $self->handlerDeviceExists($handler, $device);
+
+       if ($rc) {
+               $self->{'error'} = "closeDevice(): An error occured while closing device $device. ".
+                 "See dmesg/kernel log for more information.";
+       }
+
+       return $rc;
+}
+
+sub userExists {
+       my $self = shift;
+       my $user = shift;
+       my $group = shift;
+
+       my $users = $self->users($group);
+
+       return -1 if (!defined($users));
+
+       foreach my $_user (@{$users}) {
+               return $TRUE if ($user eq $_user);
+       }
+
+       return $FALSE;
+}
+
+sub users {
+       my $self = shift;
+       my $group = shift;
+       my @users;
+
+       return undef if (!$self->groupExists($group));
+
+       my $io = new IO::File $_SCST_GROUPS_DIR_."/$group/".$_SCST_USERS_IO_, O_RDONLY;
+
+       if (!$io) {
+               $self->{'error'} = "users(): Failed to open handler IO ".$_SCST_GROUPS_DIR_.
+                 "/$group/".$_SCST_USERS_IO_;
+               return undef;
+       }
+
+       while (my $line = <$io>) {
+               chomp $line;
+               
+               push @users, $line;
+       }
+
+       close $io;
+
+       return \@users;
+}
+
+sub addUser {
+       my $self = shift;
+       my $user = shift;
+       my $group = shift;
+
+       if (!$self->groupExists($group)) {
+               $self->{'error'} = "addUser(): Group $group does not exist";
+               return $TRUE;
+       }
+
+       if ($self->userExists($user, $group)) {
+               $self->{'error'} = "addUser(): User $user already exists in group $group";
+               return 2;
+       }
+
+       my $cmd = "add $user\n";
+
+       my $rc = $self->group_private($group, $_SCST_USERS_IO_, $cmd);
+
+       return $FALSE if ($self->{'debug'});
+       return $rc if ($rc);
+
+       $rc = !$self->userExists($user, $group);
+
+       if ($rc) {
+               $self->{'error'} = "addUser(): An error occured while adding user $user to group $group. ".
+                 "See dmesg/kernel log for more information.";
+       }
+
+       return $rc;
+}
+
+sub removeUser {
+       my $self = shift;
+       my $user = shift;
+       my $group = shift;
+
+       if (!$self->groupExists($group)) {
+               $self->{'error'} = "removeUser(): Group $group does not exist";
+               return $TRUE;
+       }
+
+       if ($self->userExists($user, $group)) {
+               $self->{'error'} = "removeUser(): User $user does not exist in group $group";
+               return 2;
+       }
+
+       my $cmd = "del $user\n";
+
+       my $rc = $self->group_private($group, $_SCST_USERS_IO_, $cmd);
+
+       return $FALSE if ($self->{'debug'});
+       return $rc if ($rc);
+
+       $rc = $self->userExists($user, $group);
+
+       if ($rc) {
+               $self->{'error'} = "removeUser(): An error occured while removing user $user ".
+                 "from group $group. See dmesg/kernel log for more information.";
+       }
+
+       return $rc;
+}
+
+sub clearUsers {
+       my $self = shift;
+       my $group = shift;
+
+       if (!$self->groupExists($group)) {
+               $self->{'error'} = "clearUsers(): Group $group does not exist";
+               return $TRUE;
+       }
+
+       my $cmd = "clear\n";
+
+       my $rc = $self->group_private($group, $_SCST_USERS_IO_, $cmd);
+
+       return $FALSE if ($self->{'debug'});
+
+       if ($rc) {
+               $self->{'error'} = "clearUsers(): An error occured while clearing users from ".
+                 "group $group. See dmesg/kernel log for more information.";
+               return $rc;
+       }
+
+       my $users = $self->users($group);
+
+       return ($#{$users} + 1);
+}
+
+sub handlerExists {
+       my $self = shift;
+       my $handler = shift;
+
+       my $handlers = $self->handlers();
+
+       foreach my $_handler (@{$handlers}) {
+               return $TRUE if ($handler eq $_handler);
+       }
+
+       return $FALSE;
+}
+
+sub handlers {
+       my $self = shift;
+       my @handlers;
+
+       my $dirHandle = new IO::Handle;
+
+       opendir $dirHandle, $_SCST_DIR_ or return undef;
+
+       foreach my $entry (readdir($dirHandle)) {
+               next if (($entry eq '.') || ($entry eq '..'));
+               if ((-d $_SCST_DIR_.'/'.$entry ) && (-f $_SCST_DIR_.'/'.$entry.'/type')) {
+                       push @handlers, $_TYPE_MAP_{$entry} if ($_TYPE_MAP_{$entry});
+               }
+       }
+
+       close $dirHandle;
+
+       return \@handlers;
+}
+
+sub groupDeviceExists {
+       my $self = shift;
+       my $device = shift;
+       my $group = shift;
+       my $lun = shift;
+       my $devices = $self->groupDevices($group);
+
+       return -1 if (!defined($devices));
+
+       if (defined($lun)) {
+               return $TRUE if ($$devices{$device} eq $lun);
+       } else {
+               return $TRUE if (defined($$devices{$device}));
+       }
+
+       return $FALSE;
+}
+
+sub groupDevices {
+       my $self = shift;
+       my $group = shift;
+       my %devices;
+       my $first = $TRUE;
+
+       if (!$self->groupExists($group)) {
+               $self->{'error'} = "groupDevices(): Group $group does not exist";
+               return undef;
+       }
+
+       my $io = new IO::File $_SCST_GROUPS_DIR_."/$group/".$_SCST_DEVICES_IO_, O_RDONLY;
+
+       if (!$io) {
+               $self->{'error'} = "groupDevices(): Failed to open handler IO ".$_SCST_GROUPS_DIR_.
+                 "/$group/".$_SCST_DEVICES_IO_;
+               return undef;
+       }
+
+       while (my $line = <$io>) {
+               chomp $line;
+
+               if ($first) {
+                       $first = $FALSE;
+                       next;
+               }
+
+               my($vname, $lun) = split(/\s+/, $line);
+               
+               $devices{$vname} = $lun;
+       }
+
+       close $io;
+
+       return \%devices;
+}
+
+sub assignDeviceToGroup {
+       my $self = shift;
+       my $device = shift;
+       my $group = shift;
+       my $lun = shift;
+
+       if (!$self->groupExists($group)) {
+               $self->{'error'} = "assignDeviceToGroup(): Group $group does not exist";
+               return $TRUE;
+       }
+
+       if ($self->groupDeviceExists($device, $group, $lun)) {
+               $self->{'error'} = "assignDeviceToGroup(): Device $device is already ".
+                 "assigned to group $group";
+               return 2;
+       }
+
+       my $cmd = "add $device $lun\n";
+
+       my $rc = $self->group_private($group, $_SCST_DEVICES_IO_, $cmd);
+
+       return $FALSE if ($self->{'debug'});
+       return $rc if ($rc);
+
+       $rc = !$self->groupDeviceExists($device, $group, $lun);
+
+       if ($rc) {
+               $self->{'error'} = "assignDeviceToGroup(): An error occured while assigning device $device ".
+                 "to group $group. See dmesg/kernel log for more information.";
+       }
+
+       return $rc;
+}
+
+sub assignDeviceToHandler {
+       my $self = shift;
+       my $device = shift;
+       my $handler = shift;
+       my $handler_io = $_IO_MAP_{$handler};
+       my $_handler = $_REVERSE_MAP_{$handler};
+
+       if (!$handler_io) {
+               $self->{'error'} = "assignDeviceToHandler(): Failed to open handler IO $handler_io or ".
+                 "handler $_handler($handler) invalid";
+               return $TRUE;
+       }
+
+       if (!$self->handlerExists($handler)) {
+               $self->{'error'} = "assignDeviceToHandler(): Handler $_handler does not exist";
+               return $TRUE;
+       }
+
+       if ($self->handlerDeviceExists($handler, $device)) {
+               $self->{'error'} = "assignDeviceToHandler(): Device $device is already assigned to handler $_handler";
+               return 2;
+       }
+
+       my $cmd = "assign $device $_handler\n";
+
+       my $rc = $self->scst_private($cmd);
+
+       return $FALSE if ($self->{'debug'});
+       return $rc if($rc);
+
+       $rc = !$self->handlerDeviceExists($handler, $device);
+
+       if ($rc) {
+               $self->{'error'} = "assignDeviceToHandler(): An error occured while assigning device $device ".
+                 "to handler $_handler. See dmesg/kernel log for more information.";
+       }
+
+       return $rc;
+}
+
+sub removeDeviceFromGroup {
+       my $self = shift;
+       my $device = shift;
+       my $group = shift;
+
+       if (!$self->groupExists($group)) {
+               $self->{'error'} = "removeDeviceFromGroup(): Group $group does not exist";
+               return $TRUE;
+       }
+
+       if (!$self->groupDeviceExists($device, $group)) {
+               $self->{'error'} = "removeDeviceFromGroup(): Device $device does not exist in group $group";
+               return 2;
+       }
+
+       my $cmd = "del $device\n";
+
+       my $rc = $self->group_private($group, $_SCST_DEVICES_IO_, $cmd);
+
+       return $FALSE if ($self->{'debug'});
+       return $rc if ($rc);
+
+       $rc = $self->groupDeviceExists($device, $group);
+
+       if ($rc) {
+               $self->{'error'} = "removeDeviceFromGroup(): An error occured while removing device $device ".
+                 "from group $group. See dmesg/kernel log for more information.";
+       }
+
+       return $rc;
+}
+
+sub clearGroupDevices {
+       my $self = shift;
+       my $group = shift;
+
+       return $TRUE if (!$self->groupExists($group));
+
+       my $cmd = "clear\n";
+
+       my $rc = $self->group_private($group, $_SCST_DEVICES_IO_, $cmd);
+
+       return $FALSE if ($self->{'debug'});
+
+       if ($rc) {
+               $self->{'error'} = "clearGroupDevices(): An error occured while clearing devices from ".
+                 "group $group. See dmesg/kernel log for more information.";
+               return $rc;
+       }
+
+       my $devices = $self->groupDevices($group);
+
+       return (keys %{$devices});
+}
+
+sub handler_private {
+       my $self = shift;
+       my $handler_io = shift;
+       my $cmd = shift;
+
+       my $io = new IO::File $handler_io, O_WRONLY;
+
+       if (!$io) {
+               print "WARNING: SCST/SCST.pm: Failed to open handler IO $handler_io, assuming disabled.\n";
+               return $FALSE;
+       }
+
+       if ($self->{'debug'}) {
+               print "DBG($$): '$handler_io' -> '$cmd'\n";
+       } else {
+               print $io "$cmd\0";
+       }
+
+       close $io;
+
+       return $FALSE;
+}
+
+sub scst_private {
+       my $self = shift;
+       my $cmd = shift;
+
+       my $io = new IO::File $_SCST_IO_, O_WRONLY;
+
+       if (!$io) {
+               $self->{'error'} = "SCST/SCST.pm: Failed to open handler IO $_SCST_IO_";
+               return $TRUE;
+       }
+
+       if ($self->{'debug'}) {
+               print "DBG($$): '$_SCST_IO_' -> '$cmd'\n";
+       } else {
+               print $io "$cmd\0";
+       }
+
+       close $io;
+
+       return $FALSE;
+}
+
+sub group_private {
+       my $self = shift;
+       my $group = shift;
+       my $file = shift;
+       my $cmd = shift;
+
+       my $io = new IO::File $_SCST_GROUPS_DIR_."/$group/".$file, O_WRONLY;
+
+       if (!$io) {
+               $self->{'error'} = "SCST/SCST.pm: Failed to open handler IO ".$_SCST_GROUPS_DIR_."/$group/".$file;
+               return $TRUE;
+       }
+
+       if ($self->{'debug'}) {
+               print "DBG($$): $_SCST_GROUPS_DIR_/$group/$file -> $cmd\n";
+       } else {
+               print $io "$cmd\0";
+       }
+
+       close $io;
+
+       return $FALSE;
+}
+
+sub checkOptions {
+       my $self = shift;
+       my $options = shift;
+       my $o_string;
+
+       return undef, $TRUE if (!$options);
+
+       foreach my $option (split(/\s+/, $options)) {
+               my $map = $_AVAILABLE_OPTIONS_{$option};
+               return undef, $FALSE if (!$map);
+               $o_string .= ",$map";
+       }
+
+       $o_string =~ s/^\,//;
+
+       return $o_string, $TRUE;
+}
+
+sub errorString {
+       my $self = shift;
+
+       return undef if (!$self->{'error'});
+
+       my $string = $self->{'error'};
+       $self->{'error'} = undef;
+
+       return $string;
+}
+
+sub cleanupString {
+       my $string = shift;
+
+       $string =~ s/^\s+//;
+       $string =~ s/\s+$//;
+
+       return $string;
+}
+
+;1 __END__
+
+=head1 NAME
+
+SCST::SCST - Generic SCST methods.
+
+=head1 SYNOPSIS
+
+    use SCST::SCST;
+
+    $p = SCST::SCST->new();
+    
+    print "Using SCST version".$p->scstVersion()."\n";
+    
+    if ($p->handlerDeviceExists($SCST::SCST::VDISK_TYPE)) {
+         print "openDevice() failed\n"
+           if ($p->openDevice($SCST::SCST::VDISK_TYPE, 'DISK01', '/vdisk/disk01.dsk'));
+    }
+    
+    undef $p;
+
+=head1 DESCRIPTION
+
+Generic SCST methods.
+
+=head2 Methods
+
+=over 5
+
+=item SCST::SCST->new();
+
+Create a new SCST object. If the argument $debug is non-zero no changes
+will be made.
+
+Arguments: (bool) $debug
+
+Returns: (object) $new
+
+=item SCST::SCST->scstVersion();
+
+Returns the version of SCST running.
+
+Arguments: void
+
+Returns: (string) $version
+
+=item SCST::SCST->groups();
+
+Returns a list of security groups configured.
+
+Arguments: void
+
+Returns: (array ref) $groups
+
+=item SCST::SCST->groupExists();
+
+Checks for a specified group's existance.
+
+Arguments: (string) $group
+
+Returns: (boolean) $groupExists
+
+=item SCST::SCST->addGroup();
+
+Adds a security group to SCST's configuration. Returns 0 upon success, 1 if
+unsuccessfull and  2 if the group already exists.
+
+Arguments: (string) $group
+
+Returns: (int) $success
+
+=item SCST::SCST->removeGroup();
+
+Removes a group from SCST's configuration. Returns 0 upon success, 1 if
+unsuccessfull and 2 if group does not exist.
+
+=item SCST::SCST->sgvStats();
+
+Returns a hash of stats gathered from /proc/scsi_tgt/sgv.
+
+Arguments: void
+
+Returns: (hash ref) $stats
+
+Hash Layout: See /proc/scsi_tgt/sgv for tokens. This methods simply hashes
+what's found there and returns it with no further processing.
+
+=item SCST::SCST->sessions();
+
+Returns a hash of current SCST initiator sessions. 
+
+Arguments: void
+
+Returns: (hash ref) $sessions
+
+Hash Layout: See /proc/scsi_tgt/sessions for tokens. This methods simply hashes
+what's found there and returns it with no further processing.
+
+=item SCST::SCST->devices();
+
+Returns a hash of devices configured without regard to device handler.
+
+Arguments: void
+
+Returns: (hash ref) $devices
+
+Hash Layout: (string) $device = (int) $handler
+
+=item SCST::SCST->handlerDevices();
+
+Returns a hash of devices configured for a specified device handler.
+
+Arguments: (int) $handler
+
+Returns: (hash ref) $devices
+
+Hash Layout: (string) $device -> SIZE = (int) $deviceSize
+             (string) $device -> PATH = (string) $devicePath
+             (string) $device -> OPTIONS = (string) $options (comma seperated)
+
+=item SCST::SCST->handlerDeviceExists();
+
+Checks for a specified device is configured for a specified device handler.
+
+Arguments: (int) $handler, (string) $device
+
+Returns: (boolean) $deviceExists
+
+=item SCST::SCST->handlerType();
+
+Return the handler type for the specified handler. Handler types are:
+
+  SCST::SCST::IOTYPE_PHYSICAL
+  SCST::SCST::IOTYPE_VIRTUAL
+  SCST::SCST::IOTYPE_PERFORMANCE
+
+Arguments: (int) $handler
+
+Returns: (int) $handler_type
+
+=item SCST::SCST->openDevice();
+
+Opens an already existing specified device for the specified device handler.
+Returns 0 upon success, 1 if unsuccessfull and 2 if the device already exists.
+
+Available options for the parameter $options are: WRITE_THROUGH, READ_ONLY, O_DIRECT
+
+Arguments: (int) $handler, (string) $device, (string) $path [, (string) $options]
+
+Returns: (int) $success
+
+=item SCST::SCST->closeDevice();
+
+Closes an open device configured for the specified device handler. Returns
+0 upon success, 1 if unsuccessfull and 2 of the device does not exist.
+
+Arguments: (int) $handler, (string) $device, (string) $path
+
+Returns: (int) $success
+
+=item SCST::SCST->userExists();
+
+Checks for a specified user with the specified security group.
+
+Arguments: (string) $user, (string) $group
+
+Returns (boolean) $userExists
+
+=item SCST::SCST->users();
+
+Returns a list of users configured for a given security group.
+
+Arguments: (string) $group
+
+Returns: (hash ref) $users
+
+=item SCST::SCST->addUser();
+
+Adds the specified user to the specified security group. Returns 0
+upon success, 1 if unsuccessfull and 2 if the user already exists.
+
+Arguments: (string) $user, (string) $group
+
+Returns: (int) $success
+
+=item SCST::SCST->removeUser();
+
+Removed the specified user from the specified security group. Returns
+0 upon success, 1 if unsuccessfull and 2 if the user does not exist.
+
+Arguments: (string) $user, (string) $group
+
+Returns: (int) $success
+
+=item SCST::SCST->clearUsers();
+
+Removes all users from the specified security group. Returns 0 upon
+success or 1 if unsuccessfull.
+
+Arguments: (string) $group
+
+Returns: (int) $success
+
+=item SCST::SCST->handlerExists();
+
+Checks if a specified device handler exists within SCST's configuration.
+
+Arguments: (int) $handler
+
+Returns: (boolean) $handlerExists
+
+=item SCST::SCST->handlers();
+
+Returns a list of configured device handlers.
+
+Arguments: void
+
+Returns: (array ref) $handlers
+
+=item SCST::SCST->groupDeviceExists();
+
+Checks if a specified device is assigned to a specified security group.
+If the optional $lun argument is specified, this method also matches
+the lun.
+
+Arguments: (string) $device, (string) $group [, (int) $lun]
+
+Returns: (boolean) $deviceExists
+
+=item SCST::SCST->groupDevices();
+
+Returns a hash if devices assigned to the specified security group.
+
+Arguments: (string) $group
+
+Returns: (hash ref) $devices
+
+Hash Layout: (string) $device = (int) $lun
+
+=item SCST::SCST->assignDeviceToGroup();
+
+Assigns the specified device to the specified security group. Returns
+0 upon success, 1 if unsuccessfull and 2 if the device has already
+been assigned to the specified security group.
+
+Arguments: (string) $device, (string) $group, (int) $lun
+
+Returns: (int) $success
+
+=item SCST::SCST->assignDeviceToHandler();
+
+Assigns specified device to specified handler. Returns 0 upon success,
+1 if unsuccessfull and 2 if the specified device is already assigned to
+the specified handler.
+
+Arguments: (string) $device, (string) $handler
+
+Returns: (int) $success
+
+=item SCST::SCST->removeDeviceFromGroup();
+
+Removes the specified device from the specified security group. Returns
+0 upon success, 1 if unsuccessfull and 2 if the device has not been
+assigned to the specified security group.
+
+Arguments: (string) $device, (string) $group
+
+Returns: (int) $success
+
+=item SCST::SCST->clearGroupDevices();
+
+Removes all devices from the specified security group. Returns 0 upon
+success or 1 if unsuccessfull.
+
+Arguments: (string) $group
+
+Returns: (int) $success
+
+=item SCST::SCST->errorString();
+
+Contains a description of the last error occured or undef if no error
+has occured or if this method has already been called once since the
+last error.
+
+Arguments: (void)
+
+Returns: (string) $error_string
+
+=back
+
+=head1 WARNING
+
+None at this time.
+
+=head1 NOTES
+
+If the $debug parameter is specified on package new(), no actions are 
+performed. Rather they are printed to STDOUT and 0 is returned.
+
+Available Device Handlers:
+
+CDROM_TYPE,
+CHANGER_TYPE,
+DISK_TYPE,
+VDISK_TYPE,
+VCDROM_TYPE,
+DISKPERF_TYPE,
+MODISK_TYPE,
+MODISKPERF_TYPE,
+TAPE_TYPE,
+TAPEPERF_TYPE
+
+To specify a device handler to a method, use the following syntax:
+
+$SCST::SCST::<handler type>
+
+For example:
+
+$SCST::SCST::MODISK_TYPE
+
+=cut