The initial commit of 0.9.5-pre1 version
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Tue, 14 Nov 2006 12:56:57 +0000 (12:56 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Tue, 14 Nov 2006 12:56:57 +0000 (12:56 +0000)
git-svn-id: https://scst.svn.sourceforge.net/svnroot/scst/trunk@33 d57e44dd-8a1f-0410-8b47-8ef2f437770f

12 files changed:
scstadmin/Changes [new file with mode: 0644]
scstadmin/LICENSE [new file with mode: 0644]
scstadmin/README [new file with mode: 0644]
scstadmin/SCST/SCST.pm [new file with mode: 0644]
scstadmin/examples/scst.conf [new file with mode: 0644]
scstadmin/examples/scst_example.sql [new file with mode: 0644]
scstadmin/init.d/scst [new file with mode: 0755]
scstadmin/scst.conf [new file with mode: 0644]
scstadmin/scst_db/scst_db [new file with mode: 0755]
scstadmin/scst_db/scst_db.conf [new file with mode: 0644]
scstadmin/scst_db/scst_schema.sql [new file with mode: 0644]
scstadmin/scstadmin [new file with mode: 0755]

diff --git a/scstadmin/Changes b/scstadmin/Changes
new file mode 100644 (file)
index 0000000..6bf7ba0
--- /dev/null
@@ -0,0 +1,17 @@
+Changes since 0.9.2-pre2:
+
+- List SCST sessions
+- Verify specified config file against live configuration and show differences
+- Write config file based on live configuration
+- Force-apply configuration, even config deletions with a force flag.
+- Added new option types
+- Added support for specifying a blocksize
+- Added "-reload-config" to init script
+
+Changes since 0.9.5-pre0:
+
+- Renamed scst to scstadmin
+- Fixed writeConfiguration() to properly write DEVICE lines with no options
+- Removed the comment which prevented a target from being enabled in enableTarget()
+- Updated init.d/scst to use scstadmin instead of scst_db
+- Fixup of README file
diff --git a/scstadmin/LICENSE b/scstadmin/LICENSE
new file mode 100644 (file)
index 0000000..08ddefd
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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; either version 2 of the License, or
+    (at your option) any later version.
+
+    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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
+
diff --git a/scstadmin/README b/scstadmin/README
new file mode 100644 (file)
index 0000000..16a0334
--- /dev/null
@@ -0,0 +1,53 @@
+SCST Configuration/Administration scripts. Here you'll find scstadmin which reads
+a configuration file /etc/scst.conf, or one if your choosing. With it you can manually
+or automatically configure every aspect of SCST incuding enabling/disabling target mode
+on your target SCSI controller. The old directory contains scst_db which uses a mysql
+backend database to configure SCST but doesn't handle all the things scstadmin handles.
+The perl module SCST::SCST is very generic and tries to handle error checking as well.
+
+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/
+
+       scstadmin       Script which can accept operations on a command line or from
+                       a configuration file. See scst.conf. For command help,
+                               #> scstadmin
+
+       old             Script which configures SCST using configurations
+                       found in a database. See the examples directory database
+                       schema and example data. You will need to edit the
+                       config file scst_db.conf to refect your environment.
+
+       scst.conf       Configuration file for scst script. Usually stored
+                       in /etc.
+
+       examples        Configuration examples.
+
+       init.d/scst     Init script to start SCST on boot which uses scstadmin.
+
+
+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
+a daemon process which would except network connections and issue SCST commands. A
+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:
+
+#> 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
+
+#> /etc/init.d/scst -reload-config
+
+will also not remove configurations from a running system.
+
+
+Mark.Buechler@gmail.com
+
diff --git a/scstadmin/SCST/SCST.pm b/scstadmin/SCST/SCST.pm
new file mode 100644 (file)
index 0000000..b64837d
--- /dev/null
@@ -0,0 +1,1011 @@
+package SCST::SCST;
+
+# Author:      Mark R. Buechler
+# Copyright (c) 2005, 2006 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_DISK_FILE_IO_  = $_SCST_DIR_.'/disk_fileio/disk_fileio';
+my $_SCST_CDROM_FILE_IO_ = $_SCST_DIR_.'/cdrom_fileio/cdrom_fileio';
+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_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';
+
+my @_AVAILABLE_OPTIONS_  = ('WRITE_THROUGH', 'O_DIRECT', 'READ_ONLY', 'NULLIO', 'NV_CACHE');
+
+use vars qw(@ISA @EXPORT $VERSION $CDROM_TYPE $CHANGER_TYPE $DISK_TYPE $DISKFILE_TYPE
+            $CDROMFILE_TYPE $DISKPERF_TYPE $MODISK_TYPE $MODISKPERF_TYPE $TAPE_TYPE
+            $TAPEPERF_TYPE);
+
+$CDROM_TYPE      = 1;
+$CHANGER_TYPE    = 2;
+$DISK_TYPE       = 3;
+$DISKFILE_TYPE   = 4;
+$CDROMFILE_TYPE  = 5;
+$DISKPERF_TYPE   = 6;
+$MODISK_TYPE     = 7;
+$MODISKPERF_TYPE = 8;
+$TAPE_TYPE       = 9;
+$TAPEPERF_TYPE   = 10;
+
+$VERSION = 0.6;
+
+my $_SCST_MIN_MAJOR_   = 0;
+my $_SCST_MIN_MINOR_   = 9;
+my $_SCST_MIN_RELEASE_ = 5;
+
+my %_IO_MAP_ = ($CDROM_TYPE => $_SCST_CDROM_IO_,
+               $CHANGER_TYPE => $_SCST_CHANGER_IO_,
+               $DISK_TYPE => $_SCST_DISK_IO_,
+               $DISKFILE_TYPE => $_SCST_DISK_FILE_IO_,
+               $CDROMFILE_TYPE => $_SCST_CDROM_FILE_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_);
+
+my %_TYPE_MAP_ = ('dev_cdrom' => $CDROM_TYPE,
+                 'dev_changer' => $CHANGER_TYPE,
+                 'dev_disk' => $DISK_TYPE,
+                 'disk_fileio' => $DISKFILE_TYPE,
+                 'cdrom_fileio' => $CDROMFILE_TYPE,
+                 'dev_disk_perf' => $DISKPERF_TYPE,
+                 'dev_modisk' => $MODISK_TYPE,
+                 'dev_modisk_perf' => $MODISKPERF_TYPE,
+                 'dev_tape' => $TAPE_TYPE,
+                 'dev_tape_perf' => $TAPEPERF_TYPE);
+
+sub new {
+       my $this = shift;
+       my $debug = shift;
+       my $badVersion = $TRUE;
+       
+       my $class = ref($this) || $this;
+       my $self = {};
+
+       bless($self, $class);
+
+       $self->{'debug'} = $debug if $debug;
+
+       my $scstVersion = $self->scstVersion();
+
+       my($major, $minor, $release) = split(/\./, $scstVersion, 3);
+
+       $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;
+       return $TRUE if (!$io);
+
+       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;
+       return $TRUE if (!$io);
+
+       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;
+
+       return undef if (!$io);
+
+       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;
+
+       return undef if (!$io);
+
+       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;
+
+       return undef if (!$io);
+
+       while (my $line = <$io>) {
+               chomp $line;
+
+               if ($first) {
+                       $first = $FALSE;
+                       next;
+               }
+
+               my($vname, $handler) = split(/\s+/, $line);
+               $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;
+
+       return undef if (!$handler_io);
+       return undef if (!$self->handlerExists($handler));
+
+       my $io = new IO::File $handler_io, O_RDONLY;
+       return undef if (!$io);
+
+       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'} = $self->cleanupString($options);
+               $devices{$vname}->{'SIZE'} = $self->cleanupString($size);
+               $devices{$vname}->{'PATH'} = $self->cleanupString($path);
+               $devices{$vname}->{'BLOCKSIZE'} = $self->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 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};
+
+       return $TRUE if ($self->checkOptions($options));
+       return $TRUE if (!$handler_io);
+       return $TRUE if (!$self->handlerExists($handler));
+       return 2 if ($self->handlerDeviceExists($handler, $device));
+
+       $options = $self->cleanupString($options);
+
+       my $cmd = "open $device $path $blocksize $options\n";
+
+       $cmd = $self->cleanupString($cmd);
+
+       my $rc = $self->handler_private($handler_io, $cmd);
+
+       return $FALSE if ($self->{'debug'});
+       return $rc if ($rc);
+       return !$self->handlerDeviceExists($handler, $device);
+}
+
+sub closeDevice {
+       my $self = shift;
+       my $handler = shift;
+       my $device = shift;
+       my $path = shift;
+       my $handler_io = $_IO_MAP_{$handler};
+
+       return $TRUE if (!$handler_io);
+       return $TRUE if (!$self->handlerExists($handler));
+       return 2 if (!$self->handlerDeviceExists($handler, $device));
+
+       my $cmd = "close $device $path\n";
+
+       my $rc = $self->handler_private($handler_io, $cmd);
+
+       return $FALSE if ($self->{'debug'});
+       return $rc if ($rc);
+       return $self->handlerDeviceExists($handler, $device);
+}
+
+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;
+       return undef if (!$io);
+
+       while (my $line = <$io>) {
+               chomp $line;
+               
+               push @users, $line;
+       }
+
+       close $io;
+
+       return \@users;
+}
+
+sub addUser {
+       my $self = shift;
+       my $user = shift;
+       my $group = shift;
+
+       return $TRUE if (!$self->groupExists($group));
+       return 2 if ($self->userExists($user, $group));
+
+       my $cmd = "add $user\n";
+
+       my $rc = $self->group_private($group, $_SCST_USERS_IO_, $cmd);
+
+       return $FALSE if ($self->{'debug'});
+       return $rc if ($rc);
+       return !$self->userExists($user, $group);
+}
+
+sub removeUser {
+       my $self = shift;
+       my $user = shift;
+       my $group = shift;
+
+       return $TRUE if (!$self->groupExists($group));
+       return 2 if (!$self->userExists($user, $group));
+
+       my $cmd = "del $user\n";
+
+       my $rc = $self->group_private($group, $_SCST_USERS_IO_, $cmd);
+
+       return $FALSE if ($self->{'debug'});
+       return $rc if ($rc);
+       return $self->userExists($user, $group);
+}
+
+sub clearUsers {
+       my $self = shift;
+       my $group = shift;
+
+       return $TRUE if (!$self->groupExists($group));
+
+       my $cmd = "clear\n";
+
+       my $rc = $self->group_private($group, $_SCST_USERS_IO_, $cmd);
+
+       return $FALSE if ($self->{'debug'});
+       return $rc if ($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;
+
+       return undef if (!$self->groupExists($group));
+
+       my $io = new IO::File $_SCST_GROUPS_DIR_."/$group/".$_SCST_DEVICES_IO_, O_RDONLY;
+       return undef if (!$io);
+
+       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;
+
+       return $TRUE if (!$self->groupExists($group));
+       return 2 if ($self->groupDeviceExists($device, $group, $lun));
+
+       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);
+       return !$self->groupDeviceExists($device, $group, $lun);
+}
+
+sub assignDeviceToHandler {
+       my $self = shift;
+       my $device = shift;
+       my $handler = shift;
+       my $handler_io = $_IO_MAP_{$handler};
+       
+       return $TRUE if (!$handler_io);
+       return $TRUE if (!$self->handlerExists($handler));
+       return 2 if ($self->handlerDeviceExists($handler, $device));
+
+       my $cmd = "assign $device $handler\n";
+
+       my $rc = $self->scst_private($cmd);
+
+       return $FALSE if ($self->{'debug'});
+       return $rc if($rc);
+       return !$self->handlerDeviceExists($handler, $device);
+}
+
+sub removeDeviceFromGroup {
+       my $self = shift;
+       my $device = shift;
+       my $group = shift;
+
+       return $TRUE if (!$self->groupExists($group));
+       return 2 if (!$self->groupDeviceExists($device, $group));
+
+       my $cmd = "del $device\n";
+
+       my $rc = $self->group_private($group, $_SCST_DEVICES_IO_, $cmd);
+
+       return $FALSE if ($self->{'debug'});
+       return $rc if ($rc);
+       return $self->groupDeviceExists($device, $group);
+}
+
+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'});
+       return $rc if ($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;
+       return $TRUE if (!$io);
+
+       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;
+        return $TRUE if (!$io);
+
+       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;
+       return $TRUE if (!$io);
+
+       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;
+
+       return if (!$options);
+
+       foreach my $option (split(/\s+/, $options)) {
+               foreach my $avail (@_AVAILABLE_OPTIONS_) {
+                       return $FALSE if ($avail eq $option);
+               }
+       }
+
+       return $TRUE;
+}
+
+sub cleanupString {
+       my $self = shift;
+       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::DISKFILE_TYPE)) {
+         print "openDevice() failed\n"
+           if ($p->openDevice($SCST::SCST::DISKFILE_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->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
+
+=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,
+DISKFILE_TYPE,
+CDROMFILE_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
diff --git a/scstadmin/examples/scst.conf b/scstadmin/examples/scst.conf
new file mode 100644 (file)
index 0000000..c348095
--- /dev/null
@@ -0,0 +1,91 @@
+# Automatically generated by scst.
+
+[HANDLER disk_fileio]
+DEVICE GW15K000,/dev/evms/GW15K000,512
+DEVICE GW15K001,/dev/evms/GW15K001,512
+DEVICE MP15K004,/dev/evms/MP15K004,512
+DEVICE MP15K005,/dev/evms/MP15K005,512
+DEVICE MP15K006,/dev/evms/MP15K006,512
+DEVICE MP15K007,/dev/evms/MP15K007,512
+DEVICE MP15K008,/dev/evms/MP15K008,512
+DEVICE MP15K009,/dev/evms/MP15K009,512
+DEVICE MP15K00a,/dev/evms/MP15K00a,512
+DEVICE MP15K00b,/dev/evms/MP15K00b,512
+DEVICE MP15K00c,/dev/evms/MP15K00c,512
+DEVICE MP15K00d,/dev/evms/MP15K00d,512
+DEVICE MP15K00e,/dev/evms/MP15K00e,512
+DEVICE MS10K006,/dev/evms/MS10K002,512
+DEVICE MS15K000,/dev/evms/MS15K000,512
+DEVICE MS15K001,/dev/evms/MS15K001,512
+DEVICE MS15K002,/dev/evms/MS15K002,512
+DEVICE MS15K005,/dev/evms/MS15K003,512
+DEVICE RG10K000,/dev/evms/RG10K000,512
+DEVICE RG72K001,/dev/evms/RG72K001,512
+
+[HANDLER cdrom_fileio]
+
+[GROUP CORBIN2_a]
+USER 21:00:00:e0:8b:11:8d:8a
+
+[GROUP CORBIN3_a]
+USER 21:00:00:e0:8b:03:9e:1a
+
+[GROUP MENTASM_a]
+USER 21:00:00:e0:8b:11:06:8a
+
+[GROUP PC1_a]
+USER 21:00:00:e0:8b:11:3f:8d
+
+[GROUP PC2_a]
+USER 21:00:00:e0:8b:03:d8:4a
+
+[GROUP PC3_a]
+USER 21:00:00:e0:8b:13:ba:01
+
+[GROUP PC4_a]
+USER 21:00:00:e0:8b:11:a6:8b
+
+[GROUP RAISTLIN_a]
+USER 21:00:00:e0:8b:11:75:8b
+
+[ASSIGNMENT CORBIN2_a]
+DEVICE MP15K009,0
+DEVICE MS10K006,1
+DEVICE RG10K000,2
+DEVICE RG72K001,3
+
+[ASSIGNMENT CORBIN3_a]
+DEVICE MP15K00d,0
+
+[ASSIGNMENT Default]
+DEVICE GW15K000,0
+DEVICE GW15K001,1
+
+[ASSIGNMENT MENTASM_a]
+DEVICE MP15K006,0
+DEVICE MP15K008,1
+DEVICE MP15K00e,2
+
+[ASSIGNMENT PC1_a]
+DEVICE MS15K000,0
+
+[ASSIGNMENT PC2_a]
+DEVICE MS15K005,0
+
+[ASSIGNMENT PC3_a]
+DEVICE MP15K00c,0
+
+[ASSIGNMENT PC4_a]
+DEVICE MP15K005,0
+DEVICE MP15K007,1
+
+[ASSIGNMENT RAISTLIN_a]
+DEVICE MP15K00a,0
+DEVICE MP15K00b,1
+
+[TARGETS enable]
+HOST 0x50060b0000397178
+HOST 0x50060b000039717a
+
+[TARGETS disable]
+
diff --git a/scstadmin/examples/scst_example.sql b/scstadmin/examples/scst_example.sql
new file mode 100644 (file)
index 0000000..b397328
--- /dev/null
@@ -0,0 +1,185 @@
+-- MySQL dump 10.10
+--
+-- Host: localhost    Database: scst
+-- ------------------------------------------------------
+-- Server version      5.0.26-Debian_1-log
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `assignments`
+--
+
+DROP TABLE IF EXISTS `assignments`;
+CREATE TABLE `assignments` (
+  `device_id` int(8) NOT NULL default '0',
+  `type_id` char(2) default NULL,
+  `group_id` int(4) NOT NULL default '0',
+  `host_id` int(2) NOT NULL default '0',
+  `target_id` int(2) NOT NULL default '0',
+  `target_lun` int(3) NOT NULL default '0',
+  PRIMARY KEY  (`device_id`,`group_id`,`host_id`,`target_id`,`target_lun`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `assignments`
+--
+
+LOCK TABLES `assignments` WRITE;
+/*!40000 ALTER TABLE `assignments` DISABLE KEYS */;
+INSERT INTO `assignments` VALUES (5,'MS',13,0,0,0),(5,'MP',11,0,0,0),(13,'MP',6,0,0,0),(0,'GW',1,0,0,0),(1,'GW',1,0,0,1),(12,'MP',12,0,0,0),(6,'MP',8,0,0,0),(8,'MP',8,0,0,1),(9,'MP',9,0,0,0),(10,'MP',10,0,0,0),(11,'MP',10,0,0,1),(7,'MP',11,0,0,1),(14,'MP',8,0,0,2),(0,'MS',7,0,0,0),(1,'RG',9,0,0,3),(0,'RG',9,0,0,2),(6,'MS',9,0,0,1);
+/*!40000 ALTER TABLE `assignments` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `device_types`
+--
+
+DROP TABLE IF EXISTS `device_types`;
+CREATE TABLE `device_types` (
+  `type_id` char(2) NOT NULL default '',
+  `type_name` char(100) NOT NULL default '',
+  PRIMARY KEY  (`type_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `device_types`
+--
+
+LOCK TABLES `device_types` WRITE;
+/*!40000 ALTER TABLE `device_types` DISABLE KEYS */;
+INSERT INTO `device_types` VALUES ('GW','Gateway Communication Device'),('MP','Mirrored Pair Device'),('SV','Snapshot Device'),('SD','Single Disk Device (Unprotected)'),('SS','Stripe Set (VERY Unprotected)'),('RG','Raid 5 Group Device'),('MS','Mirrored Stripe Device');
+/*!40000 ALTER TABLE `device_types` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `devices`
+--
+
+DROP TABLE IF EXISTS `devices`;
+CREATE TABLE `devices` (
+  `device_id` int(8) NOT NULL default '0',
+  `type_id` char(2) NOT NULL default '',
+  `perf_id` char(3) NOT NULL default '',
+  `md_uuid` char(40) default NULL,
+  `device_path` char(100) default NULL,
+  `options` char(50) default NULL,
+  `blocksize` int(6) default NULL,
+  `scst_handlr_id` int(2) NOT NULL default '0',
+  PRIMARY KEY  (`device_id`,`type_id`)
+) ENGINE=MyISAM AUTO_INCREMENT=29 DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `devices`
+--
+
+LOCK TABLES `devices` WRITE;
+/*!40000 ALTER TABLE `devices` DISABLE KEYS */;
+INSERT INTO `devices` VALUES (7,'MP','15K','','/dev/evms/MP15K007',NULL,NULL,2),(5,'MP','15K','','/dev/evms/MP15K005',NULL,NULL,2),(6,'MP','15K','','/dev/evms/MP15K006',NULL,NULL,2),(4,'MP','15K','','/dev/evms/MP15K004',NULL,NULL,2),(8,'MP','15K','','/dev/evms/MP15K008',NULL,NULL,2),(9,'MP','15K','','/dev/evms/MP15K009',NULL,NULL,2),(10,'MP','15K','','/dev/evms/MP15K00a',NULL,NULL,2),(11,'MP','15K','','/dev/evms/MP15K00b',NULL,NULL,2),(12,'MP','15K','','/dev/evms/MP15K00c',NULL,NULL,2),(0,'GW','15K','','/dev/evms/GW15K000',NULL,NULL,2),(1,'GW','15K','','/dev/evms/GW15K001',NULL,NULL,2),(13,'MP','15K','','/dev/evms/MP15K00d',NULL,NULL,2),(14,'MP','15K','','/dev/evms/MP15K00e',NULL,NULL,2),(6,'MS','10K','','/dev/evms/MS10K002',NULL,NULL,2),(5,'MS','15K','','/dev/evms/MS15K003',NULL,NULL,2),(1,'RG','72K','','/dev/evms/RG72K001',NULL,NULL,2),(2,'MS','15K','','/dev/evms/MS15K002',NULL,NULL,2),(1,'MS','15K','','/dev/evms/MS15K001',NULL,NULL,2),(0,'MS','15K','','/dev/evms/MS15K000',NULL,NULL,2),(0,'RG','10K','','/dev/evms/RG10K000',NULL,NULL,2);
+/*!40000 ALTER TABLE `devices` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `group_users`
+--
+
+DROP TABLE IF EXISTS `group_users`;
+CREATE TABLE `group_users` (
+  `group_id` int(16) NOT NULL default '0',
+  `user_id` char(32) NOT NULL default '',
+  PRIMARY KEY  (`group_id`,`user_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `group_users`
+--
+
+LOCK TABLES `group_users` WRITE;
+/*!40000 ALTER TABLE `group_users` DISABLE KEYS */;
+INSERT INTO `group_users` VALUES (6,'20:00:00:e0:8b:03:9e:1a'),(6,'21:00:00:e0:8b:03:9e:1a'),(7,'20:00:00:e0:8b:11:3f:8d'),(7,'21:00:00:e0:8b:11:3f:8d'),(8,'20:00:00:e0:8b:11:06:8a'),(8,'21:00:00:e0:8b:11:06:8a'),(9,'20:00:00:e0:8b:11:8d:8a'),(9,'21:00:00:e0:8b:11:8d:8a'),(10,'20:00:00:e0:8b:11:75:8b'),(10,'21:00:00:e0:8b:11:75:8b'),(11,'20:00:00:e0:8b:11:a6:8b'),(11,'21:00:00:e0:8b:11:a6:8b'),(12,'20:00:00:e0:8b:13:ba:01'),(12,'21:00:00:e0:8b:13:ba:01'),(13,'20:00:00:e0:8b:03:d8:4a'),(13,'21:00:00:e0:8b:03:d8:4a');
+/*!40000 ALTER TABLE `group_users` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `perf_types`
+--
+
+DROP TABLE IF EXISTS `perf_types`;
+CREATE TABLE `perf_types` (
+  `perf_id` char(3) NOT NULL default '',
+  `perf_name` char(100) NOT NULL default '',
+  PRIMARY KEY  (`perf_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `perf_types`
+--
+
+LOCK TABLES `perf_types` WRITE;
+/*!40000 ALTER TABLE `perf_types` DISABLE KEYS */;
+INSERT INTO `perf_types` VALUES ('15K','15K RPM UltraSCSI-2'),('10F','10K RPM Fibre'),('72I','7200 RPM ATA/SATA'),('54I','5400 RPM ATA/SATA'),('10I','10K RPM ATA/SATA');
+/*!40000 ALTER TABLE `perf_types` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `scst_handlers`
+--
+
+DROP TABLE IF EXISTS `scst_handlers`;
+CREATE TABLE `scst_handlers` (
+  `scst_handlr_id` int(2) NOT NULL default '0',
+  `handler_name` char(32) NOT NULL default '',
+  `autoload` enum('N','Y') NOT NULL default 'N',
+  PRIMARY KEY  (`scst_handlr_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `scst_handlers`
+--
+
+LOCK TABLES `scst_handlers` WRITE;
+/*!40000 ALTER TABLE `scst_handlers` DISABLE KEYS */;
+INSERT INTO `scst_handlers` VALUES (1,'disk','N'),(2,'disk_fileio','Y'),(3,'cdrom','N'),(4,'changer','N'),(5,'disk_perf','N'),(6,'modisk','N'),(7,'modisk_perf','N'),(8,'tape','N'),(9,'tape_perf','N');
+/*!40000 ALTER TABLE `scst_handlers` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `security_groups`
+--
+
+DROP TABLE IF EXISTS `security_groups`;
+CREATE TABLE `security_groups` (
+  `group_id` int(4) NOT NULL auto_increment,
+  `group_name` char(100) NOT NULL default '',
+  PRIMARY KEY  (`group_id`)
+) ENGINE=MyISAM AUTO_INCREMENT=14 DEFAULT CHARSET=latin1;
+
+--
+-- Dumping data for table `security_groups`
+--
+
+LOCK TABLES `security_groups` WRITE;
+/*!40000 ALTER TABLE `security_groups` DISABLE KEYS */;
+INSERT INTO `security_groups` VALUES (1,'Default'),(6,'CORBIN3_a'),(12,'PC3_a'),(7,'PC1_a'),(8,'MENTASM_a'),(9,'CORBIN2_a'),(10,'RAISTLIN_a'),(11,'PC4_a'),(13,'PC2_a');
+/*!40000 ALTER TABLE `security_groups` ENABLE KEYS */;
+UNLOCK TABLES;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2006-11-06 20:47:39
diff --git a/scstadmin/init.d/scst b/scstadmin/init.d/scst
new file mode 100755 (executable)
index 0000000..3e60625
--- /dev/null
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin
+SCST_CMD=/usr/local/sbin/scstadmin
+SCST_CFG=/etc/scst.conf
+
+# Modules to load/unload
+SCST_MODULES="scst_fileio scst_disk scst_cdrom"
+
+OPTIONS=""
+
+test -x $SCST_CMD -a -f $SCST_CFG || exit 0
+
+case "$1" in
+  start)
+       echo -n "Loading and configuring SCSI Target Mid-level: scst "
+
+       modprobe qla2x00tgt || { echo "[qla2x00tgt failed]" ; exit 1 ; }
+
+       for module in ${SCST_MODULES}; do
+               modprobe ${module} || { echo "[${module} failed]" ; exit 1 ; }
+       done
+
+       $SCST_CMD -config $SCST_CFG
+
+       RC=$?
+
+       if [ $RC -ne 0 ];
+       then
+               echo "[config failed]"
+       fi
+
+       echo "."
+       ;;
+  stop)        
+       echo -n "Stopping SCSI Target Mid-level: scst "
+
+       for module in ${SCST_MODULES}; do
+               rmmod ${module} || { echo "[${module} failed]" ; }
+       done
+
+       rmmod qla2x00tgt || { echo "[qla2x00tgt failed]" ; }
+       rmmod scsi_tgt || { echo "[scsi_tgt failed]" ; }
+       
+       echo "."
+       ;;
+  force-reload|restart)
+       $0 stop
+       sleep 1
+       $0 start
+       ;;
+  reload-config)
+       echo -n "Reloading configuration: scst "
+
+       $SCST_CMD -config $SCST_CFG
+
+       RC=$?
+
+       if [ $RC -ne 0 ];
+       then
+               echo "[config failed]"
+       fi
+
+       echo "."
+       ;;
+  *)
+       echo "Usage: /etc/init.d/scst {start|stop|restart|force-reload|reload-config}"
+       exit 1
+esac
+
+exit 0
diff --git a/scstadmin/scst.conf b/scstadmin/scst.conf
new file mode 100644 (file)
index 0000000..eb71ff5
--- /dev/null
@@ -0,0 +1,51 @@
+# SCST Configurator v0.2
+
+# Handler devices
+[HANDLER disk]
+# DEVICE <H:C:I:L>
+
+[HANDLER disk_fileio]
+# DEVICE <vname>,<path>,<WRITE_THROUGH|READ_ONLY|O_DIRECT|NV_CACHE|NULLIO>,<block size>
+DEVICE DISK01,/tmp/test1.dsk,READ_ONLY|WRITE_THROUGH
+DEVICE DISK02,/tmp/test2.dsk
+DEVICE DISK03,/tmp/test3.dsk,O_DIRECT
+DEVICE DISK04,/tmp/test4.dsk,,512
+
+[HANDLER disk_perf]
+
+[HANDLER cdrom]
+# DEVICE <H:C:I:L>
+
+[HANDLER changer]
+# DEVICE <H:C:I:L>
+
+[HANDLER modisk]
+# DEVICE <H:C:I:L>
+
+[HANDLER modisk_perf]
+
+[HANDLER tape]
+# DEVICE <H:C:I:L>
+
+[HANDLER tape_perf]
+
+# Security Group
+[GROUP Test]
+# USER <user>
+USER 50060b000001369d
+
+# Device Assignments
+[ASSIGNMENT Test]
+# DEVICE <device>,<vlun>
+DEVICE DISK01,0
+DEVICE DISK02,1
+DEVICE DISK03
+
+# Target to enable
+[TARGETS enable]
+HOST 50:06:0B:00:00:39:71:78
+HOST host4
+
+# Targets to disable
+[TARGETS disable]
+HOST host5
diff --git a/scstadmin/scst_db/scst_db b/scstadmin/scst_db/scst_db
new file mode 100755 (executable)
index 0000000..6b3c6f9
--- /dev/null
@@ -0,0 +1,556 @@
+#!/usr/bin/perl
+$Version  = 'SCST DB Configurator v0.51';
+
+# Configures SCST
+#
+# Written by Mark R. Buechler 12/07/04
+
+sub usage
+  {
+    die <<"EndUsage";
+$Version
+
+Usage:
+General Operations
+     -config <config>     : Configure SCST given the specified configuration file.
+     -check               : Check database configuration against current configuration.
+
+Options
+     -ForceConfig         : Force all confuration changes, even deletions (DANGER!).
+
+Debugging (limited support)
+     -debug               : Debug mode - don\'t do anything destructive.
+     
+Examples:
+     Configure SCST:
+       scst_db config scst_db.conf
+
+EndUsage
+  }
+
+use SCST::SCST;
+use Getopt::Long;
+use IO::Handle;
+use IO::File;
+use DBI;
+use POSIX;
+use strict;
+
+my $_DEF_CONFIG_ = '/etc/scst_db.conf';
+
+my $TRUE  = 1;
+my $FALSE = 0;
+
+my $_MAX_LUNS_      = 255;
+my $_DEFAULT_GROUP_ = 'Default';
+
+my $_MDADM_  = '/sbin/mdadm';
+my $_MDSTAT_ = '/proc/mdstat';
+my $_MD_DEV_ = '/dev/';
+
+my $SCST;
+my $DEVICES;
+my %USERS;
+my %ASSIGNMENTS;
+my %HANDLERS;
+my %GROUPS;
+my $_DEBUG_;
+
+my %MD_DEVICES;
+
+my %_HANDLER_MAP_ = ('cdrom' => $SCST::SCST::CDROM_TYPE,
+                    'changer' => $SCST::SCST::CHANGER_TYPE,
+                    'disk' => $SCST::SCST::DISK_TYPE,
+                    'disk_fileio' => $SCST::SCST::DISKFILE_TYPE,
+                    'cdrom_fileio' => $SCST::SCST::CDROMFILE_TYPE,
+                    'disk_perf' => $SCST::SCST::DISKPERF_TYPE,
+                    'modisk' => $SCST::SCST::MODISK_TYPE,
+                    'modisk_perf' => $SCST::SCST::MODISKPERF_TYPE,
+                    'tape' => $SCST::SCST::TAPE_TYPE,
+                    'tape_perf' => $SCST::SCST::TAPEPERF_TYPE);
+                    
+my %_REVERSE_MAP_ = ($SCST::SCST::CDROM_TYPE => 'cdrom',
+                    $SCST::SCST::CHANGER_TYPE => 'changer',
+                    $SCST::SCST::DISK_TYPE => 'disk',
+                    $SCST::SCST::DISKFILE_TYPE => 'disk_fileio',
+                    $SCST::SCST::CDROMFILE_TYPE => 'cdrom_fileio',
+                    $SCST::SCST::DISKPERF_TYPE => 'disk_perf',
+                    $SCST::SCST::MODISK_TYPE => 'modisk',
+                    $SCST::SCST::MODISKPERF_TYPE => 'modisk_perf',
+                    $SCST::SCST::TAPE_TYPE => 'tape',
+                    $SCST::SCST::TAPEPERF_TYPE => 'tape_perf');
+
+$SIG{INT} = \&commitSuicide;
+
+POSIX::setsid();
+
+&main();
+
+sub getArgs {
+       my $applyConfig;
+       my $checkConfig;
+       my $forceConfig;
+
+       my $p = new Getopt::Long::Parser;
+
+       if (!$p->getoptions('config:s'          => \$applyConfig,
+                           'check'             => \$checkConfig,
+                           'ForceConfig'       => \$forceConfig,
+                           'debug'             => \$_DEBUG_)) {
+               &usage();
+       }
+
+       $applyConfig = $_DEF_CONFIG_ if (defined($applyConfig) && !$applyConfig);
+       $forceConfig = $TRUE if (defined($forceConfig));
+
+       return ($applyConfig, $checkConfig, $forceConfig);
+}
+
+sub main {
+       my $rc;
+
+       STDOUT->autoflush(1);
+
+       # We need to run as root
+       if ( $> ) {die("This program must run as root.\n");}
+
+       my ($config, $check, $force) = getArgs();
+
+       $SCST = new SCST::SCST($_DEBUG_);
+
+       readCurrentConfig();
+       scanDevices();
+
+       SWITCH: {
+               $config && do {
+                       $rc = applyConfiguration($config, $force, $check);
+                       last SWITCH;
+               };
+
+               print "No valid operations specified.\n";
+               usage();
+               exit $TRUE;
+       }
+
+       print "All done.\n";
+
+       exit $rc;
+}
+
+sub readCurrentConfig {
+       print "Collecting current configuration.. ";
+
+       my $eHandlers = $SCST->handlers();
+
+       foreach my $handler (@{$eHandlers}) {
+               $HANDLERS{$handler}++; # For quick lookups
+       }
+
+       $DEVICES = $SCST->devices();
+       my $_eGroups = $SCST->groups();
+
+       foreach my $group (@{$_eGroups}) {
+               $GROUPS{$group}++; # For quick lookups
+               $ASSIGNMENTS{$group} = $SCST->groupDevices($group);
+               my $eUsers = $SCST->users($group);
+
+               foreach my $user (@{$eUsers}) {
+                       $USERS{$group}->{$user}++; # For quick lookups
+               }
+       }
+
+       print "done.\n";
+}
+
+sub scanDevices {
+       my $ch = new IO::Handle;
+       
+       print "Scanning available system devices.. ";
+       
+       my $mdstat = new IO::File $_MDSTAT_, O_RDONLY;
+       
+       die("FATAL: Unable to gather a list of available md devices.\n") if (!$mdstat);
+       
+       while (my $line = <$mdstat>) {
+               if ($line =~ /^md/) {
+                       my($md, undef) = split(/\:/, $line);
+                       $md = cleanupString($md);
+                       $md = $_MD_DEV_.$md;
+
+                       open $ch, "$_MDADM_ --detail $md -b|" or 
+                         die("FATAL: Unable to gather information for md device $md.\n");
+                       
+                       my $buffer = <$ch>;
+                       chomp $buffer;
+                       
+                       close $ch;
+
+                       my(undef, $uuid_t) = split(/UUID\=/, $buffer);
+                       $uuid_t = cleanupString($uuid_t);
+
+                       $MD_DEVICES{$uuid_t} = $md;
+               }
+       }
+       
+       print "done.\n";
+}
+
+sub applyConfiguration {
+       my $confile = shift;
+       my $force = shift;
+       my $check = shift;
+       my $config = readConfig($confile);
+       my $errs;
+       my $changes = 0;
+
+       my $database = $$config{'DATABASE'}->{'default'}->{'NAME'};
+       my $user     = $$config{'DATABASE'}->{'default'}->{'USERNAME'};
+       my $password = $$config{'DATABASE'}->{'default'}->{'PASSWORD'};
+       my $hostname = $$config{'DATABASE'}->{'default'}->{'HOST'};
+       
+       my $db = DBI->connect("DBI:mysql:database=$database;host=$hostname", "$user", "$password");
+       
+       my $sth = $db->prepare("SELECT device_id, device_path, options, blocksize, type_id, perf_id, md_uuid, handler_name ".
+                              "FROM devices, scst_handlers ".
+                              "WHERE devices.scst_handlr_id = scst_handlers.scst_handlr_id ".
+                              "ORDER BY device_id");
+       die("FATAL: Unable to obtain list of devices: ".$sth->errstr."\n") if (!$sth->execute());
+       
+       # Open new devices and assign them to handlers..
+       while (my $ref = $sth->fetchrow_hashref()) {
+               if (!$HANDLERS{$_HANDLER_MAP_{$ref->{'handler_name'}}}) {
+                       print "WARNING: Handler '".$ref->{'handler_name'}."' does not exist.\n";
+                       $errs += 1;
+                       next;
+               }
+               
+               my $vname = translateDeviceName($ref->{'device_id'}, $ref->{'type_id'}, $ref->{'perf_id'});
+
+               next if (defined($$DEVICES{$vname}));
+
+               my $options = $ref->{'options'};
+               $options =~ s/\s+//; $options =~ s/\|/,/;
+
+               if ($ref->{'md_uuid'}) {
+                       print "Using UUID for device ".$ref->{'device_id'}." ($vname).\n";
+
+                       my $handler = $ref->{'handler_name'};
+                       my $uuid = findMDDevice($ref->{'md_uuid'});
+                       my $blocksize = $ref->{'blocksize'};
+
+                       $changes++;
+
+                       if ($check) {
+                               print "\t-> New device '$handler:$vname', UUID: '$uuid', ".
+                                 "Options: '$options' Blocksize: $blocksize.\n";
+                       } else {
+                               $errs += addDevice($handler, $vname, $uuid, $options, $blocksize);
+                       }
+               } elsif ($ref->{'device_path'}) {
+                       print "Using path for device ".$ref->{'device_id'}." ($vname).\n";
+
+                       my $handler = $ref->{'handler_name'};
+                       my $path = $ref->{'device_path'};
+                       my $blocksize = $ref->{'blocksize'};
+
+                       $changes++;
+
+                       if ($check) {
+                               print "\t-> New device '$handler:$vname', Path: '$path', ".
+                                 "Options: '$options', Blocksize: $blocksize.\n";
+                       } else {
+                               $errs += addDevice($handler, $vname, $path, $options, $blocksize);
+                       }
+               } else {
+                       print "FATAL: No UUID or path configured for device ".$ref->{'device_id'}." ($vname).\n";
+                       $errs += 1;
+                       next;
+               }
+       }
+
+       my $sth = $db->prepare("SELECT group_id, group_name FROM security_groups");
+       die("FATAL: Unable to obtain list of groups: ".$sth->errstr."\n") if (!$sth->execute());
+       
+       # Create new groups and add users..
+       while (my $ref = $sth->fetchrow_hashref()) {
+               if (!defined($GROUPS{$ref->{'group_name'}})) {
+                       my $group = $ref->{'group_name'};
+
+                       $changes++;
+
+                       if ($check) {
+                               print "\t-> New group definition '$group'.\n";
+                       } else {
+                               $errs += addGroup($group);
+                       }
+               }
+       }
+
+       my $sth = $db->prepare("SELECT group_name, user_id ".
+                              "FROM group_users, security_groups ".
+                              "WHERE security_groups.group_id = group_users.group_id");
+       die("FATAL: Unable to obtain list of users: ".$sth->errstr."\n") if (!$sth->execute());
+
+       while (my $ref = $sth->fetchrow_hashref()) {
+               if (!defined($USERS{$ref->{'group_name'}}->{$ref->{'user_id'}})) {
+                       my $group = $ref->{'group_name'};
+                       my $user = $ref->{'user_id'};
+
+                       $changes++;
+
+                       if ($check) {
+                               print "\t-> New user definition '$user' for group '$group'.\n";
+                       } else {
+                               $errs += addUser($group, $user);
+                       }
+               }
+       }
+
+       my $sth_a = $db->prepare("SELECT device_id, type_id, group_name, host_id, target_id, target_lun ".
+                                "FROM assignments, security_groups ".
+                                "WHERE assignments.group_id = security_groups.group_id");
+       die("FATAL: Unable to obtain list of assignments: ".$sth_a->errstr."\n") if (!$sth_a->execute());
+
+       # Assign new devices to groups..
+       while (my $ref_a = $sth_a->fetchrow_hashref()) {
+               if (!defined($GROUPS{$ref_a->{'group_name'}})) {
+                       print "WARNING: Unable to assign to non-existant group '".$ref_a->{'group_name'}."'.\n";
+                       $errs += 1;
+                       next;
+               }
+
+               my $sth_d = $db->prepare("SELECT type_id, perf_id FROM devices ".
+                                        "WHERE device_id = ".$ref_a->{'device_id'}." ".
+                                        "AND type_id = '".$ref_a->{'type_id'}."'");
+               die("FATAL: Unable to obtain list of device information: ".$sth_d->errstr."\n") if (!$sth_d->execute());
+               
+               my $ref_d = $sth_d->fetchrow_hashref();
+               
+               my $vname = translateDeviceName($ref_a->{'device_id'}, $ref_d->{'type_id'}, $ref_d->{'perf_id'});
+
+               my $_assignments = $ASSIGNMENTS{$ref_a->{'group_name'}};
+               next if (defined($$_assignments{$vname}));
+
+               my $group = $ref_a->{'group_name'};
+               my $lun = $ref_a->{'target_lun'};
+
+               $changes++;
+
+               if ($check) {
+                       print "\t-> New device assignment '$vname' for group '$group' at LUN $lun.\n";
+               } else {
+                       $errs += assignDevice($group, $vname, $lun);
+               }
+       }
+
+       print "Encountered $errs error(s) while processing.\n" if ($errs);
+
+       undef $db;
+
+       if ($check) {
+               print "Configuration checked, $changes difference(s) found with current configuation.\n";
+       } else {
+               print "Configuration applied.\n";
+       }
+
+       return $TRUE if ($errs);
+       return $FALSE;
+}
+
+sub addDevice {
+       my $handler = shift;
+       my $device = shift;
+       my $path = shift;
+       my $options = shift;
+
+       $options =~ s/\,/ /;
+
+       my $_handler = $_HANDLER_MAP_{$handler};
+
+       if (defined($$DEVICES{$device})) {
+               print "WARNING: Device '$device' already defined.\n";
+               return $TRUE;
+       }
+
+       print "Opening virtual device '$device' at path '$path' using handler '$handler'..\n";
+
+       if ($SCST->openDevice($_handler, $device, $path, $options)) {
+               print "WARNING: Failed to open virtual device '$device' at path '$path' (Options: $options).\n";
+               return $TRUE;
+       }
+
+       $$DEVICES{$device} = $_handler;
+
+       return $FALSE;
+}
+
+sub addGroup {
+       my $group = shift;
+
+       if (defined($GROUPS{$group})) {
+               print "WARNING: Group '$group' already exists.\n";
+               return $TRUE;
+       }
+
+       print "Creating security group '$group'..\n";
+
+       if ($SCST->addGroup($group)) {
+               print "WARNING: Failed to create security group '$group'.\n";
+               return $TRUE;
+       }
+
+       $GROUPS{$group}++;
+
+       return $FALSE;
+}
+
+sub addUser {
+       my $group = shift;
+       my $user = shift;
+
+       if (!defined($GROUPS{$group})) {
+               print "WARNING: Failed to add user '$user' to group '$group', group does not exist.\n";
+               return $TRUE;
+       }
+
+       if (defined($USERS{$group}->{$user})) {
+               print "WARNING: User '$user' already exists in security group '$group'.\n";
+               return $TRUE;
+       }
+
+       print "Adding user '$user' to security group '$group'..\n";
+
+       if ($SCST->addUser($user, $group)) {
+               print "WARNING: Failed to add user '$user' to security group '$group'.\n";
+               return $TRUE;
+       }
+
+       $USERS{$group}->{$user}++;
+
+       return $FALSE;
+}
+
+sub assignDevice {
+       my $group = shift;
+       my $device = shift;
+       my $lun = shift;
+       my %allLuns;
+
+       # Put luns into something easier to parse..
+       foreach my $_group (keys %ASSIGNMENTS) {
+               my $_gAssigns = $ASSIGNMENTS{$_group};
+
+               foreach my $_device (keys %{$_gAssigns}) {
+                       @{$allLuns{$_group}}[$$_gAssigns{$_device}] = $_device;
+               }
+       }
+
+       # Use the next available LUN if none specified
+       if ($lun !~ /\d+/) {
+               $lun = ($#{$allLuns{$group}} + 1);
+               if ($lun > $_MAX_LUNS_) {
+                       print "ERROR: Unable to use next available LUN of $lun, lun out of range.\n";
+                       return $TRUE;
+               }
+
+               print "Device '$device': Using next available LUN of $lun for group '$group'.\n";
+       }
+                       
+       if (($lun < 0) || ($lun > $_MAX_LUNS_)) {
+               print "ERROR: Unable to assign device '$device', lun '$lun' is out of range.\n";
+               return $TRUE;
+       }
+
+       if (!defined($$DEVICES{$device})) {
+               print "WARNING: Unable to assign non-existant device '$device' to group '$group'.\n";
+               return $TRUE;
+       }
+
+       if (@{$allLuns{$group}}[$lun]) {
+               print "ERROR: Device '$device': Lun '$lun' is already assigned to device '".@{$allLuns{$group}}[$lun]."'.\n";
+               return $TRUE;
+       }
+
+       print "Assign virtual device '$device' to group '$group' at LUN '$lun'..\n";
+
+       if ($SCST->assignDeviceToGroup($device, $group, $lun)) {
+               print "WARNING: Failed to assign device '$device' to group '$group'.\n";
+               return $TRUE;
+       }
+
+       if (!defined($ASSIGNMENTS{$group})) {
+               my %assignments_t;
+               $ASSIGNMENTS{$group} = \%assignments_t;
+       }
+
+       my $_assignments = $ASSIGNMENTS{$group};
+
+       $$_assignments{$device} = $lun; 
+
+       return $FALSE;
+}
+
+sub readConfig {
+       my $confile = shift;
+       my %config;
+       my $section;
+       my $arg;
+
+       my $io = new IO::File $confile, O_RDONLY;
+
+       die("FATAL: Unable to open specified configuration file $confile: $!\n") if (!$io);
+
+       while (my $line = <$io>) {
+               ($line, undef) = split(/\#/, $line, 2);
+               $line = cleanupString($line);
+
+               if ($line =~ /^\[(.*)\]$/) {
+                       ($section, $arg) = split(/\s+/, $1, 2);
+               } elsif ($section && $arg && $line) {
+                       my($parameter, $value) = split(/\s+/, $line, 2);
+                       $config{$section}->{$arg}->{$parameter} = $value;
+               }
+       }
+
+       close $io;
+
+       return \%config;
+}
+
+sub findMDDevice {
+       my $uuid = shift;
+
+       return $MD_DEVICES{$uuid};
+}
+
+sub translateDeviceName {
+       my $device_id = shift;
+       my $type_id = shift;
+       my $perf_id = shift;
+       
+       my $device_id = sprintf("%lx", $device_id);
+       my $device_id_t = $device_id;
+
+       for (my $t = length($device_id); $t <= 2; $t++) {
+               $device_id_t = "0$device_id_t";
+       }
+
+       $device_id = $device_id_t;
+
+       return $type_id.$perf_id.$device_id;
+}
+
+sub cleanupString {
+       my $string = shift;
+
+       $string =~ s/^\s+//;
+       $string =~ s/\s+$//;
+
+       return $string;
+}
+
+# Hey! Stop that!
+sub commitSuicide {
+       print "\n\nBut I haven\'t finished yet!\n";
+       exit 1;
+}
diff --git a/scstadmin/scst_db/scst_db.conf b/scstadmin/scst_db/scst_db.conf
new file mode 100644 (file)
index 0000000..9569e9c
--- /dev/null
@@ -0,0 +1,8 @@
+# SCST DB Configurator v0.1
+
+# Database definition
+[DATABASE default]
+NAME scst
+USERNAME scst
+PASSWORD
+HOST localhost
\ No newline at end of file
diff --git a/scstadmin/scst_db/scst_schema.sql b/scstadmin/scst_db/scst_schema.sql
new file mode 100644 (file)
index 0000000..031bf09
--- /dev/null
@@ -0,0 +1,115 @@
+-- MySQL dump 10.10
+--
+-- Host: localhost    Database: scst
+-- ------------------------------------------------------
+-- Server version      5.0.26-Debian_1-log
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `assignments`
+--
+
+DROP TABLE IF EXISTS `assignments`;
+CREATE TABLE `assignments` (
+  `device_id` int(8) NOT NULL default '0',
+  `type_id` char(2) default NULL,
+  `group_id` int(4) NOT NULL default '0',
+  `host_id` int(2) NOT NULL default '0',
+  `target_id` int(2) NOT NULL default '0',
+  `target_lun` int(3) NOT NULL default '0',
+  PRIMARY KEY  (`device_id`,`group_id`,`host_id`,`target_id`,`target_lun`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `device_types`
+--
+
+DROP TABLE IF EXISTS `device_types`;
+CREATE TABLE `device_types` (
+  `type_id` char(2) NOT NULL default '',
+  `type_name` char(100) NOT NULL default '',
+  PRIMARY KEY  (`type_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `devices`
+--
+
+DROP TABLE IF EXISTS `devices`;
+CREATE TABLE `devices` (
+  `device_id` int(8) NOT NULL default '0',
+  `type_id` char(2) NOT NULL default '',
+  `perf_id` char(3) NOT NULL default '',
+  `md_uuid` char(40) default NULL,
+  `device_path` char(100) default NULL,
+  `options` char(50) default NULL,
+  `blocksize` int(6) default NULL,
+  `scst_handlr_id` int(2) NOT NULL default '0',
+  PRIMARY KEY  (`device_id`,`type_id`)
+) ENGINE=MyISAM AUTO_INCREMENT=29 DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `group_users`
+--
+
+DROP TABLE IF EXISTS `group_users`;
+CREATE TABLE `group_users` (
+  `group_id` int(16) NOT NULL default '0',
+  `user_id` char(32) NOT NULL default '',
+  PRIMARY KEY  (`group_id`,`user_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `perf_types`
+--
+
+DROP TABLE IF EXISTS `perf_types`;
+CREATE TABLE `perf_types` (
+  `perf_id` char(3) NOT NULL default '',
+  `perf_name` char(100) NOT NULL default '',
+  PRIMARY KEY  (`perf_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `scst_handlers`
+--
+
+DROP TABLE IF EXISTS `scst_handlers`;
+CREATE TABLE `scst_handlers` (
+  `scst_handlr_id` int(2) NOT NULL default '0',
+  `handler_name` char(32) NOT NULL default '',
+  `autoload` enum('N','Y') NOT NULL default 'N',
+  PRIMARY KEY  (`scst_handlr_id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+--
+-- Table structure for table `security_groups`
+--
+
+DROP TABLE IF EXISTS `security_groups`;
+CREATE TABLE `security_groups` (
+  `group_id` int(4) NOT NULL auto_increment,
+  `group_name` char(100) NOT NULL default '',
+  PRIMARY KEY  (`group_id`)
+) ENGINE=MyISAM AUTO_INCREMENT=14 DEFAULT CHARSET=latin1;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2006-11-06 20:47:29
diff --git a/scstadmin/scstadmin b/scstadmin/scstadmin
new file mode 100755 (executable)
index 0000000..e4e1f91
--- /dev/null
@@ -0,0 +1,1270 @@
+#!/usr/bin/perl
+$Version  = 'SCST Configurator v0.6';
+
+# Configures SCST
+#
+# Written by Mark R. Buechler 12/07/04
+
+sub usage
+  {
+    die <<"EndUsage";
+$Version
+
+Usage:
+General Operations
+     -config <config>     : Configure SCST given the specified configuration file.
+     -ClearConfig         : Clear all SCST configuration.
+     -WriteConfig <file>  : Writes the current configuration out to the specified file.
+     -checkConfig <file>  : Checks the saved configuration in the specified file.
+     -sessions            : List current initiator sessions.
+     
+Target Driver Operations
+     -enable <wwn|host>   : Enable target mode for driver at specified WWN or host.
+     -disable <wwn|host>  : Disable target mode for driver at specified WWN or host.
+
+Devices Operations
+     -adddev <device>     : Adds a device to a handler.
+         -handler <handler>
+         -path <path>
+         -options <options>
+         -blocksize <bytes>
+     -RemoveDev <device>  : Remove a device from a handler.
+         -handler <handler>
+Users Operations
+     -adduser <user>      : Adds a user to a security group.
+         -group <group>
+     -RemoveUser <user>   : Delete a user from a security group.
+         -group <group>
+     -ClearUsers          : Clear all users from a given security group.
+         -group <group>
+
+Groups Operations
+     -addgroup <group>    : Add a given group to available security groups.
+     -RemoveGroup <group> : Remove a give group from available security groups.
+     
+Assignments Operations
+     -assigndev <device>  : Assign a given device to a security group.
+         -group <group>
+         -lun <lun>
+     -ReleaseDev <device> : Remove a given device from a security group.
+         -group <group>
+     -ClearDevs           : Clear all device assignments for a security group.
+         -group <group>
+
+Options
+     -ForceConfig         : Force all confuration changes, even deletions (DANGER!).
+  
+Debugging (limited support)
+     -debug               : Debug mode - don\'t do anything destructive.
+
+Available Handlers:
+      disk, disk_fileio, disk_perf, cdrom, changer, modisk, modisk_perf, tape, tape_perf
+
+Available Options for create and open:
+      WRITE_THROUGH, READ_ONLY, O_DIRECT, NULLIO, NV_CACHE
+     
+Examples:
+     Enable target mode for fibre card specifying its WWN
+       scstadmin -enable 50:06:0B:00:00:39:71:78
+
+     Disable target mode for SCSI host specifying host number
+       scstadmin -disable host4 
+
+     Create a new security group:
+       scstadmin -addgroup HOST01
+       
+     Create a device given an already existing disk file:
+       scstadmin -adddev DISK01 -handler disk_fileio -path /vdisks/disk01.dsk -options READ_ONLY,WRITE_THROUGH
+       
+     Assign a device to a security group:
+       scstadmin -assigndev DISK01 -group HOST01 -lun 1
+
+EndUsage
+  }
+
+use SCST::SCST;
+use Getopt::Long;
+use IO::File;
+use IO::Dir;
+use POSIX;
+use strict;
+
+my $_DEF_CONFIG_ = '/etc/scst.conf';
+
+my $TRUE  = 1;
+my $FALSE = 0;
+
+my $_MAX_LUNS_      = 255;
+my $_DEFAULT_GROUP_ = 'Default';
+
+my $_SCSI_CLASS_    = '/sys/class/scsi_host';
+my $_FC_CLASS_      = '/sys/class/fc_host';
+
+my $SCST;
+my $DEVICES;
+my $TARGETS;
+my %USERS;
+my %ASSIGNMENTS;
+my %HANDLERS;
+my %GROUPS;
+my $_DEBUG_;
+
+my %_HANDLER_MAP_ = ('cdrom' => $SCST::SCST::CDROM_TYPE,
+                    'changer' => $SCST::SCST::CHANGER_TYPE,
+                    'disk' => $SCST::SCST::DISK_TYPE,
+                    'disk_fileio' => $SCST::SCST::DISKFILE_TYPE,
+                    'cdrom_fileio' => $SCST::SCST::CDROMFILE_TYPE,
+                    'disk_perf' => $SCST::SCST::DISKPERF_TYPE,
+                    'modisk' => $SCST::SCST::MODISK_TYPE,
+                    'modisk_perf' => $SCST::SCST::MODISKPERF_TYPE,
+                    'tape' => $SCST::SCST::TAPE_TYPE,
+                    'tape_perf' => $SCST::SCST::TAPEPERF_TYPE);
+                    
+my %_REVERSE_MAP_ = ($SCST::SCST::CDROM_TYPE => 'cdrom',
+                    $SCST::SCST::CHANGER_TYPE => 'changer',
+                    $SCST::SCST::DISK_TYPE => 'disk',
+                    $SCST::SCST::DISKFILE_TYPE => 'disk_fileio',
+                    $SCST::SCST::CDROMFILE_TYPE => 'cdrom_fileio',
+                    $SCST::SCST::DISKPERF_TYPE => 'disk_perf',
+                    $SCST::SCST::MODISK_TYPE => 'modisk',
+                    $SCST::SCST::MODISKPERF_TYPE => 'modisk_perf',
+                    $SCST::SCST::TAPE_TYPE => 'tape',
+                    $SCST::SCST::TAPEPERF_TYPE => 'tape_perf');
+
+$SIG{INT} = \&commitSuicide;
+
+POSIX::setsid();
+
+&main();
+
+sub getArgs {
+       my $applyConfig;
+       my $forceConfig;
+       my $clearConfig;
+       my $writeConfig;
+       my $checkConfig;
+       my $showSessions;
+       my $addDev;
+       my $devPath;
+       my $removeDev;
+       my $addUser;
+       my $removeUser;
+       my $clearUsers;
+       my $addGroup;
+       my $removeGroup;
+       my $assignDev;
+       my $releaseDev;
+       my $clearDevs;
+       my $devLun;
+       my $handler;
+       my $group;
+       my $options;
+       my $blocksize;
+       my $enable;
+       my $disable;
+
+       my $p = new Getopt::Long::Parser;
+
+       if (!$p->getoptions('config:s'          => \$applyConfig,
+                           'ClearConfig'       => \$clearConfig,
+                           'ForceConfig'       => \$forceConfig,
+                           'WriteConfig=s'     => \$writeConfig,
+                           'checkConfig=s'     => \$checkConfig,
+                           'sessions'          => \$showSessions,
+                           'adddev=s'          => \$addDev,
+                           'path=s'            => \$devPath,
+                           'RemoveDev=s'       => \$removeDev,
+                           'lun=s'             => \$devLun,
+                           'adduser=s'         => \$addUser,
+                           'RemoveUser=s'      => \$removeUser,
+                           'ClearUsers'        => \$clearUsers,
+                           'addgroup=s'        => \$addGroup,
+                           'RemoveGroup=s'     => \$removeGroup,
+                           'assigndev=s'       => \$assignDev,
+                           'ReleaseDev=s'      => \$releaseDev,
+                           'ClearDevs'         => \$clearDevs,
+                           'handler=s'         => \$handler,
+                           'group=s'           => \$group,
+                           'options=s'         => \$options,
+                           'blocksize=s'       => \$blocksize,
+                           'enable=s'          => \$enable,
+                           'disable=s'         => \$disable,
+                           'debug'             => \$_DEBUG_)) {
+               &usage();
+       }
+
+       if ((defined($enable) && !$enable) || (defined($disable) && !$disable)) {
+               print "Argument -enable/-disable requires a WWN or host.\n\n";
+               usage();
+       }
+
+       if ($handler && !$_HANDLER_MAP_{$handler}) {
+               print "Invalid handler '$handler' specified. Available handlers are:\n\n";
+               foreach my $_handler (keys %_HANDLER_MAP_) {
+                       print "\t$_handler\n";
+               }
+               print "\n";
+               exit 1;
+       }
+
+       if ($addDev && !($handler && $devPath)) {
+               print "Please specify -handler and -path with -adddev.\n\n";
+               usage();
+       }
+
+       if (defined($blocksize) && !$blocksize) {
+               print "Please specify bytes with -blocksize.\n\n";
+               usage();
+       }
+
+       if ($blocksize && !$addDev) {
+               print "Please specify -adddev with -blocksize.\n";
+               usage();
+       }
+
+       if (defined($forceConfig) && !defined($applyConfig)) {
+               print "Please specify -config with -ForceConfig.\n\n";
+               usage();
+       }
+
+       if ($removeDev && !$handler) {
+               print "Please specify -handler with -RemoveDev.\n\n";
+               usage();
+       }
+
+       if ($addUser && !defined($group)) {
+               print "Please specify -group with -adduser.\n\n";
+               usage();
+       }
+
+       if ($removeUser && !defined($group)) {
+               print "Please specify -group with -RemoveUser.\n\n";
+               usage();
+       }
+
+       if ($clearUsers && !defined($group)) {
+               print "Please specify -group with -ClearUsers.\n\n";
+               usage();
+       }
+
+       if ($assignDev && !(defined($group) && defined($devLun))) {
+               print "Please specify -group and -lun with -assigndev.\n\n";
+               usage();
+       }
+
+       if ($releaseDev && !defined($group)) {
+               print "Please specify -group with -RemoveDev.\n\n";
+               usage();
+       }
+
+       if ($clearDevs && !defined($group)) {
+               print "Please specify -group with -ClearDevs.\n\n";
+               usage();
+       }
+
+       if (defined($writeConfig) && !$writeConfig) {
+               print "Please specify a file name to write configuration to..\n\n";
+               usage();
+       }
+
+       $forceConfig = $TRUE if (defined($forceConfig));
+       $showSessions = $TRUE if (defined($showSessions));
+
+       $enable =~ tr/A-Z/a-z/; $disable =~ tr/A-Z/a-z/;
+
+       if ((defined($showSessions) + defined($addDev) + defined($removeDev) +
+            defined($addUser) + defined($enable) + defined($disable) +
+            defined($removeUser) + defined($clearUsers) + defined($assignDev) +
+            defined($releaseDev) + defined($clearDevs) + defined($applyConfig) +
+            defined($clearConfig) + defined($writeConfig) + defined($checkConfig)) > 1) {
+               print "Please specify only one operation at a time.\n";
+               usage();
+       }
+
+       $applyConfig = $_DEF_CONFIG_ if (defined($applyConfig) && !$applyConfig);
+       $checkConfig = $_DEF_CONFIG_ if (defined($checkConfig) && !$checkConfig);
+
+       return ($enable, $disable, $addDev, $devPath, $devLun, $removeDev, $addUser, $removeUser,
+               $clearUsers, $addGroup, $removeGroup, $assignDev, $releaseDev, $clearDevs,
+               $handler, $group, $options, $blocksize, $applyConfig, $forceConfig,
+               $clearConfig, $writeConfig, $checkConfig, $showSessions);
+}
+
+sub main {
+       my $rc;
+
+       STDOUT->autoflush(1);
+
+       # We need to run as root
+       if ( $> ) {die("This program must run as root.\n");}
+
+       my ($enable, $disable, $addDev, $devPath, $devLun, $removeDev, $addUser, $removeUser,
+           $clearUsers, $addGroup, $removeGroup, $assignDev, $releaseDev, $clearDevs,
+           $handler, $group, $options, $blocksize, $applyConfig, $forceConfig,
+           $clearConfig, $writeConfig, $checkConfig, $showSessions) = getArgs();
+
+       $SCST = new SCST::SCST($_DEBUG_);
+
+       readCurrentConfig();
+
+       SWITCH: {
+               $applyConfig && do {
+                       if ($forceConfig) {
+                               $rc = applyConfiguration($applyConfig, $FALSE, $TRUE);
+                               die("Configuration errors found, aborting.\n") if ($rc);
+
+                               print "\nConfiguration will apply in 10 seconds, type ctrl-c to abort..\n";
+                               sleep 10;
+                       }
+
+                       $rc = applyConfiguration($applyConfig, $forceConfig, $FALSE);
+                       last SWITCH;
+               };
+               $checkConfig && do {
+                       $rc = applyConfiguration($checkConfig, $FALSE, $TRUE);
+                       last SWITCH;
+               };
+               $writeConfig && do {
+                       $rc = writeConfiguration($writeConfig);
+                       last SWITCH;
+               };
+               $showSessions && do {
+                       $rc = showSessions();
+                       last SWITCH;
+               };
+               defined($clearConfig) && do {
+                       $rc = clearConfiguration();
+                       last SWITCH;
+               };
+               $addDev && do {
+                       $rc = addDevice($handler, $addDev, $devPath, $options, $blocksize);
+                       last SWITCH;
+               };
+               $removeDev && do {
+                       $rc = removeDevice($handler, $removeDev);
+                       last SWITCH;
+               };
+               $addUser && do {
+                       $rc = addUser($group, $addUser);
+                       last SWITCH;
+               };
+               $removeUser && do {
+                       $rc = removeUser($group, $removeUser);
+                       last SWITCH;
+               };
+               defined($clearUsers) && do {
+                       $rc = clearUsers($group);
+                       last SWITCH;
+               };
+               $addGroup && do {
+                       $rc = addGroup($addGroup);
+                       last SWITCH;
+               };
+               $removeGroup && do {
+                       $rc = removeGroup($removeGroup);
+                       last SWITCH;
+               };
+               $assignDev && do {
+                       $rc = assignDevice($group, $assignDev, $devLun);
+                       last SWITCH;
+               };
+               $releaseDev && do {
+                       $rc = releaseDevice($group, $releaseDev);
+                       last SWITCH;
+               };
+               defined($clearDevs) && do {
+                       $rc = clearDevices($group);
+                       last SWITCH;
+               };
+               $enable && do {
+                       $enable = unformatTarget($enable);
+                       $rc = enableTarget($enable, $TRUE);
+                       last SWITCH;
+               };
+               $disable && do {
+                       $disable = unformatTarget($disable);
+                       $rc = enableTarget($disable, $FALSE);
+                       last SWITCH;
+               };
+
+               print "No valid operations specified.\n";
+               usage();
+               exit $TRUE;
+       }
+
+       print "All done.\n";
+
+       exit $rc;
+}
+
+sub readCurrentConfig {
+       print "Collecting current configuration.. ";
+
+       my $eHandlers = $SCST->handlers();
+
+       foreach my $handler (@{$eHandlers}) {
+               $HANDLERS{$handler}++; # For quick lookups
+       }
+
+       $TARGETS = targets();
+
+       $DEVICES = $SCST->devices();
+       my $_eGroups = $SCST->groups();
+
+       foreach my $group (@{$_eGroups}) {
+               $GROUPS{$group}++; # For quick lookups
+               $ASSIGNMENTS{$group} = $SCST->groupDevices($group);
+               my $eUsers = $SCST->users($group);
+
+               foreach my $user (@{$eUsers}) {
+                       $USERS{$group}->{$user}++; # For quick lookups
+               }
+       }
+
+       print "done.\n";
+}
+
+sub writeConfiguration {
+       my $file = shift;
+
+       if (-f $file) {
+               if (!unlink $file) {
+                       print "Failed to save current configuration, specified ".
+                         "file exists and cannot be deleted.\n";
+                       return 1;
+               }
+       }
+
+       my $io = new IO::File $file, O_CREAT|O_WRONLY;
+
+       if (!$io) {
+               print "Failed to save configuration to file '$file': $!\n";
+               return 1;
+       }
+
+       print "Writing current configuration to file '$file'.. ";
+
+       print $io "# Automatically generated by scst.\n\n";
+
+       # Device information
+       foreach my $handler (sort keys %HANDLERS) {
+               print $io "[HANDLER ".$_REVERSE_MAP_{$handler}."]\n";
+
+               my $devices = $SCST->handlerDevices($handler);
+
+               foreach my $device (sort keys %{$devices}) {
+                       my $options = $$devices{$device}->{'OPTIONS'};
+                       $options =~ s/\,/\|/;
+
+                       print $io "DEVICE $device,".$$devices{$device}->{'PATH'};
+                       print $io ",$options";
+                       print $io ",".$$devices{$device}->{'BLOCKSIZE'};
+                       print $io "\n";
+               }
+
+               print $io "\n";
+       }
+
+       # User configuration
+       foreach my $group (sort keys %USERS) {
+               print $io "[GROUP $group]\n";
+
+               foreach my $user (keys %{$USERS{$group}}) {
+                       print $io "USER $user\n";
+               }
+
+               print $io "\n";
+       }
+
+       # Assignments configuration
+       foreach my $group (sort keys %ASSIGNMENTS) {
+               print $io "[ASSIGNMENT $group]\n";
+
+               my $pointer = $ASSIGNMENTS{$group};
+               foreach my $device (sort keys %{$pointer}) {
+                       print $io "DEVICE $device,".$$pointer{$device}."\n";
+               }
+
+               print $io "\n";
+       }
+
+       # Targets configuration
+       foreach my $type ('enable', 'disable') {
+               print $io "[TARGETS $type]\n";
+
+               foreach my $target (sort keys %{$TARGETS}) {
+                       if ((($type eq 'enable') && $$TARGETS{$target}->{'enabled'}) ||
+                           (($type eq 'disable') && !$$TARGETS{$target}->{'enabled'})) {
+                               my $f_target = formatTarget($target);
+                               print $io "HOST $f_target\n" if (!$$TARGETS{$target}->{'duplicate'});
+                       }
+               }
+
+               print $io "\n";
+       }
+
+       print "done\n";
+
+       close $io;
+
+       return 0;
+}
+
+sub applyConfiguration {
+       my $confile = shift;
+       my $force = shift;
+       my $check = shift;
+       my $config = readConfig($confile);
+       my $errs;
+       my $changes = 0;
+
+       my %used_devs;
+       my %used_users;
+       my %used_assignments;
+
+       print "Applying configurations additions..\n" if (!$check);
+       print "\n";
+
+       # Enable/Disable configured targets
+       foreach my $type (keys %{$$config{'TARGETS'}}) {
+               my $enable;
+
+               if ($type eq 'enable') {
+                       $enable = $TRUE;
+               } elsif ($type eq 'disable') {
+                       $enable = $FALSE;
+               } else {
+                       print "\t-> WARNING: Ignoring invalid TARGETS specifier '$type'. ".
+                         "Should be one of enable,disable.\n";
+                       next;
+               }
+
+               foreach my $target (@{$$config{'TARGETS'}->{$type}->{'HOST'}}) {
+                       my $i_target = unformatTarget($target);
+
+                       if (!defined($$TARGETS{$i_target})) {
+                               print "\t-> WARNING: Target '$target' not found on system.\n";
+                               $errs += 1;
+                               next;
+                       }
+
+                       next if ($enable == targetEnabled($i_target));
+
+                       if (!$enable && targetEnabled($target)) {
+                               if ($force || $check) {
+                                       print "\t-force: Target mode for '$target' is currently enabled, ".
+                                         "however configuration file wants it disabled";
+
+                                       if (!$check) {
+                                               print ", disabling.\n";
+                                               $errs += enableTarget($target, $enable);
+                                       } else {
+                                               print ".\n";
+                                       }
+
+                                       $changes++;
+                               }
+                       } else {
+                               print "\t-> Target '$target' is enabled in configuration file, ".
+                                 "however is currently disabled";
+
+                               if (!$check) {
+                                       print ", enabling.\n";
+                                       $errs += enableTarget($target, $enable);
+                               } else {
+                                       print ".\n";
+                               }
+
+                               $changes++;
+                       }
+               }
+       }
+
+       # Open new devices and assign them to handlers..
+       foreach my $entry (keys %{$$config{'HANDLER'}}) {
+               if (!$HANDLERS{$_HANDLER_MAP_{$entry}}) {
+                       print "\t-> WARNING: Handler '$entry' does not exist.\n";
+                       $errs += 1;
+                       next;
+               }
+
+               foreach my $device (@{$$config{'HANDLER'}->{$entry}->{'DEVICE'}}) {
+                       my($vname, $path, $options, $blocksize) = split(/\,/, $device);
+                       $vname = cleanupString($vname);
+                       $path = cleanupString($path);
+
+                       $options =~ s/\s+//; $options =~ s/\|/,/;
+
+                       $used_devs{$vname} = $entry;
+
+                       next if (defined($$DEVICES{$vname}));
+
+                       $changes++;
+
+                       if ($check) {
+                               print "\t-> New device '$entry:$vname' at path '$path', options '$options', blocksize $blocksize.\n";
+                               $$DEVICES{$vname} = $_HANDLER_MAP_{$entry};
+                       } else {
+                               $errs += addDevice($entry, $vname, $path, $options, $blocksize);
+                       }
+               }
+       }
+
+       # Create new groups and add users..
+       foreach my $group (keys %{$$config{'GROUP'}}) {
+               if (!defined($GROUPS{$group})) {
+                       $changes++;
+
+                       if ($check) {
+                               print "\t-> New group definition '$group.'\n";
+                               $GROUPS{$group}++;
+                       } else {
+                               $errs += addGroup($group);
+                       }
+               }
+
+               foreach my $user (@{$$config{'GROUP'}->{$group}->{'USER'}}) {
+                       $used_users{$group}->{$user}++;
+
+                       if (!defined($USERS{$group}->{$user})) {
+                               $changes++;
+
+                               if ($check) {
+                                       print "\t-> New user definition '$user' for group '$group'.\n";
+                                       $USERS{$group}->{$user}++;
+                               } else {
+                                       $errs += addUser($group, $user);
+                               }
+                       }
+               }
+       }
+
+       # Assign new devices to groups..
+       foreach my $group (keys %{$$config{'ASSIGNMENT'}}) {
+               if (!defined($GROUPS{$group})) {
+                       print "\t-> WARNING: Unable to assign to non-existant group '$group'.\n";
+                       $errs += 1;
+                       next;
+               }
+
+               foreach my $device (@{$$config{'ASSIGNMENT'}->{$group}->{'DEVICE'}}) {
+                       my($vname, $lun) = split(/\,/, $device);
+                       $vname = cleanupString($vname);
+                       $lun = cleanupString($lun);
+
+                       $used_assignments{$group}->{$vname}++;
+
+                       my $_assignments = $ASSIGNMENTS{$group};
+                       next if (defined($$_assignments{$vname}));
+
+                       $changes++;
+
+                       if ($check) {
+                               $lun = 'auto' if (!defined($lun));
+                               print "\t-> New device assignment for '$vname' to group '$group' at LUN $lun.\n";
+                       } else {
+                               $errs += assignDevice($group, $vname, $lun);
+                       }
+               }
+       }
+
+       # If -ForceConfig is used, check for configurations which we've deleted but are still active.
+       if ($force || $check) {
+               readCurrentConfig() if (!$check);
+
+               # Associations
+               foreach my $group (sort keys %ASSIGNMENTS) {
+                       if (!defined($used_assignments{$group})) {
+                               print "\t-force: Group $group has no associations in saved configuration";
+
+                               if (!$check) {
+                                       print ", clearing all associations.\n";
+                                       $errs += clearDevices($group);
+                               } else {
+                                       print ".\n";
+                               }
+
+                               $changes++;
+                       } else {
+                               my $_assignments = $ASSIGNMENTS{$group};
+
+                               foreach my $device (sort keys %{$_assignments}) {
+                                       if (!defined($used_assignments{$group}->{$device})) {
+                                               print "\t-force: Device $device is not associated with group ".
+                                                 "$group in saved configuration";
+
+                                               if (!$check) {
+                                                       print ", releasing.\n";
+                                                       $errs += releaseDevice($group, $device);
+                                               } else {
+                                                       print ".\n";
+                                               }
+
+                                               $changes++;
+                                       }
+                               }
+                       }
+               }
+
+               # Users & Groups
+               foreach my $group (sort keys %USERS) {
+                       if (!defined($used_users{$group})) {
+                               print "\t-force: Group $group does not exist in saved configuration";
+
+                               if (!$check) {
+                                       print ", removing.\n";
+                                       $errs += clearUsers($group);
+                                       $errs += removeGroup($group);
+                               } else {
+                                       print ".\n";
+                               }
+
+                               $changes++;
+                       } else {
+                               foreach my $user (sort keys %{$USERS{$group}}) {
+                                       if (!defined($used_users{$group}->{$user})) {
+                                               print "\t-force: User $user is not defined as part of group $group ".
+                                                 "in saved configuration";
+
+                                               if (!$check) {
+                                                       print ", removing.\n";
+                                                       $errs += removeUser($group, $user);
+                                               } else {
+                                                       print ".\n";
+                                               }
+
+                                               $changes++;
+                                       }
+                               }
+                       }
+               }
+
+               # Devices
+               foreach my $device (sort keys %{$DEVICES}) {
+                       if ($$DEVICES{$device} && !defined($used_devs{$device})) {
+                               # Device gone, but is it still assigned tp a group?
+                               my $isAssigned = $FALSE;
+                               foreach my $group (sort keys %used_assignments) {
+                                       if (defined($used_assignments{$group}->{$device})) {
+                                               print "\t-force: WARNING: Device $device is not defined in saved configuration, ".
+                                                 "however, it is still assigned to group $group! Ignoring removal.\n";
+                                               $isAssigned = $TRUE;
+                                       }
+                               }
+
+                               if (!$isAssigned) {
+                                       print "\t-force: Device $device is not defined in saved configuration";
+
+                                       if (!$check) {
+                                               print ", removing.\n";
+                                               $errs += removeDevice($device);
+                                       } else {
+                                               print ".\n";
+                                       }
+
+                                       $changes++;
+                               }
+                       } else {
+                               # Handler change
+                               if ($_HANDLER_MAP_{$used_devs{$device}} != $$DEVICES{$device}) {
+                                       my $handler = $used_devs{$device};
+
+                                       if ($HANDLERS{$_HANDLER_MAP_{$handler}}) {
+                                               print "\t-force: Device $device changes handler to $handler";
+
+                                               if (!$check) {
+                                                       print ", changing.\n";
+                                                       $errs += assignDeviceToHandler($device, $handler);
+
+                                               } else {
+                                                       print ".\n";
+                                               }
+
+                                               $changes++;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       print "\nEncountered $errs error(s) while processing.\n" if ($errs);
+
+       if ($check) {
+               print "Configuration checked, $changes difference(s) found with current configuration.\n";
+       } else {
+               $changes = 0 if ($_DEBUG_);
+               print "Configuration applied, $changes changes made.\n";
+       }
+
+       return $TRUE if ($errs);
+       return $FALSE;
+}
+
+sub clearConfiguration {
+       my $errs;
+
+       print "WARNING: This removes ALL applied SCST configuration and may result in data loss!\n";
+       print "If this is not what you intend, press ctrl-c now. Waiting 10 seconds.\n\n";
+       sleep 10;
+
+       print "\nRemoving all user and groups:\n\n";
+       foreach my $group (keys %GROUPS) {
+               $errs += removeGroup($group) if ($group ne $_DEFAULT_GROUP_);
+       }
+
+       print "\nRemoving all handler devices:\n\n";
+       foreach my $device (keys %{$DEVICES}) {
+               $errs += removeDevice($_REVERSE_MAP_{$$DEVICES{$device}}, $device);
+       }
+
+       print "\nEncountered $errs error(s) while processing.\n" if ($errs);
+       print "\nConfiguration cleared.\n";
+
+       return $TRUE if ($errs);
+       return $FALSE;
+}
+
+sub showSessions {
+       my $sessions = $SCST->sessions();
+
+       print "\n\tTarget Name\tInitiator Name\t\t\tGroup Name\t\tCommand Count\n";
+
+       foreach my $target (keys %{$sessions}) {
+               foreach my $group (keys %{$$sessions{$target}}) {
+                       foreach my $user (keys %{$$sessions{$target}->{$group}}) {
+                               my $commands = $$sessions{$target}->{$group}->{$user};
+
+                               print "\t$target\t$user\t\t$group\t\t$commands\n";
+                       }
+               }
+       }
+
+       print "\n";
+
+       return $FALSE;
+}
+
+sub addDevice {
+       my $handler = shift;
+       my $device = shift;
+       my $path = shift;
+       my $options = shift;
+       my $blocksize = shift;
+
+       $options =~ s/\,/ /;
+
+       my $_handler = $_HANDLER_MAP_{$handler};
+
+       if (defined($$DEVICES{$device})) {
+               print "WARNING: Device '$device' already defined.\n";
+               return $TRUE;
+       }
+
+       print "\t-> Opening virtual device '$device' at path '$path' using handler '$handler'..\n";
+
+       if ($SCST->openDevice($_handler, $device, $path, $options, $blocksize)) {
+               print "WARNING: Failed to open virtual device '$device' at path '$path'.\n";
+               return $TRUE;
+       }
+
+       $$DEVICES{$device} = $_handler;
+
+       return $FALSE;
+}
+
+sub removeDevice {
+       my $handler = shift;
+       my $device = shift;
+
+       my $_handler = $_HANDLER_MAP_{$handler};
+
+       if (!defined($$DEVICES{$device})) {
+               print "WARNING: Device '$device' not defined.\n";
+               return $TRUE;
+       }
+
+       print "\t-> Closing virtual device '$device'..\n";
+
+       if ($SCST->closeDevice($_handler, $device)) {
+               print "WARNING: Failed to close virtual device '$device'.\n";
+               return $TRUE;
+       }
+
+       undef $$DEVICES{$device};
+
+       return $FALSE;
+}
+
+sub addGroup {
+       my $group = shift;
+
+       if (defined($GROUPS{$group})) {
+               print "WARNING: Group '$group' already exists.\n";
+               return $TRUE;
+       }
+
+       print "\t-> Creating security group '$group'..\n";
+
+       if ($SCST->addGroup($group)) {
+               print "WARNING: Failed to create security group '$group'.\n";
+               return $TRUE;
+       }
+
+       $GROUPS{$group}++;
+
+       return $FALSE;
+}
+
+sub removeGroup {
+       my $group = shift;
+
+       if (!defined($GROUPS{$group})) {
+               print "WARNING: Group '$group' does not exist.\n";
+               return $TRUE;
+       }
+
+       print "\t-> Removing security group '$group'..\n";
+
+       if ($SCST->removeGroup($group)) {
+               print "WARNING: Failed to remove security group '$group'.\n";
+               return $TRUE;
+       }
+
+       undef $GROUPS{$group};
+
+       return $FALSE;
+}
+
+sub addUser {
+       my $group = shift;
+       my $user = shift;
+
+       if (!defined($GROUPS{$group})) {
+               print "WARNING: Failed to add user '$user' to group '$group', group does not exist.\n";
+               return $TRUE;
+       }
+
+       if (defined($USERS{$group}->{$user})) {
+               print "WARNING: User '$user' already exists in security group '$group'.\n";
+               return $TRUE;
+       }
+
+       print "\t-> Adding user '$user' to security group '$group'..\n";
+
+       if ($SCST->addUser($user, $group)) {
+               print "WARNING: Failed to add user '$user' to security group '$group'.\n";
+               return $TRUE;
+       }
+
+       $USERS{$group}->{$user}++;
+
+       return $FALSE;
+}
+
+sub removeUser {
+       my $group = shift;
+       my $user = shift;
+
+       if (!defined($GROUPS{$group})) {
+               print "WARNING: Failed to remove user '$user' from group '$group', group does not exist.\n";
+               return $TRUE;
+       }
+
+       if (!defined($USERS{$group}->{$user})) {
+               print "WARNING: User '$user' doesn\'t exist in security group '$group'.\n";
+               return $TRUE;
+       }
+
+       print "\t-> Removing user '$user' from security group '$group'..\n";
+
+       if ($SCST->removeUser($user, $group)) {
+               print "WARNING: Failed to add user '$user' to security group '$group'.\n";
+               return $TRUE;
+       }
+
+       undef $USERS{$group}->{$user};
+
+       return $FALSE;
+}
+
+sub clearUsers {
+       my $group = shift;
+
+       if (!defined($GROUPS{$group})) {
+               print "WARNING: Failed to clear users from group '$group', group does not exist.\n";
+               return $TRUE;
+       }
+
+       print "\t-> Clearing users from security group '$group'..\n";
+
+       if ($SCST->clearUsers($group)) {
+               print "WARNING: Failed to clear users from security group '$group'.\n";
+               return $TRUE;
+       }
+
+       undef $USERS{$group};
+
+       return $FALSE;
+}
+
+sub assignDevice {
+       my $group = shift;
+       my $device = shift;
+       my $lun = shift;
+       my %allLuns;
+
+       # Put luns into something easier to parse..
+       foreach my $_group (keys %ASSIGNMENTS) {
+               my $_gAssigns = $ASSIGNMENTS{$_group};
+
+               foreach my $_device (keys %{$_gAssigns}) {
+                       @{$allLuns{$_group}}[$$_gAssigns{$_device}] = $_device;
+               }
+       }
+
+       # Use the next available LUN if none specified
+       if ($lun !~ /\d+/) {
+               $lun = ($#{$allLuns{$group}} + 1);
+               if ($lun > $_MAX_LUNS_) {
+                       print "ERROR: Unable to use next available LUN of $lun, lun out of range.\n";
+                       return $TRUE;
+               }
+
+               print "\t-> Device '$device': Using next available LUN of $lun for group '$group'.\n";
+       }
+                       
+       if (($lun < 0) || ($lun > $_MAX_LUNS_)) {
+               print "ERROR: Unable to assign device '$device', lun '$lun' is out of range.\n";
+               return $TRUE;
+       }
+
+       if (!defined($$DEVICES{$device})) {
+               print "WARNING: Unable to assign non-existant device '$device' to group '$group'.\n";
+               return $TRUE;
+       }
+
+       if (@{$allLuns{$group}}[$lun]) {
+               print "ERROR: Device '$device': Lun '$lun' is already assigned to device '".@{$allLuns{$group}}[$lun]."'.\n";
+               return $TRUE;
+       }
+
+       print "\t-> Assign virtual device '$device' to group '$group' at LUN '$lun'..\n";
+
+       if ($SCST->assignDeviceToGroup($device, $group, $lun)) {
+               print "WARNING: Failed to assign device '$device' to group '$group'.\n";
+               return $TRUE;
+       }
+
+       if (!defined($ASSIGNMENTS{$group})) {
+               my %assignments_t;
+               $ASSIGNMENTS{$group} = \%assignments_t;
+       }
+
+       my $_assignments = $ASSIGNMENTS{$group};
+
+       $$_assignments{$device} = $lun; 
+
+       return $FALSE;
+}
+
+sub releaseDevice {
+       my $group = shift;
+       my $device = shift;
+
+       if (!defined($GROUPS{$group})) {
+               print "WARNING: Failed to release device '$device' from group '$group', group does not exist.\n";
+               return $TRUE;
+       }
+
+       if (!defined($$DEVICES{$device})) {
+               print "WARNING: Failed to release device '$device', device not defined.\n";
+               return $TRUE;
+       }
+
+       print "\t-> Release virtual device '$device' from group '$group'..\n";
+
+       if ($SCST->removeDeviceFromGroup($device, $group)) {
+               print "WARNING: Failed to release device '$device' from group '$group'.\n";
+               return $TRUE;
+       }
+
+       my $_assignments = $ASSIGNMENTS{$group};
+
+       undef $$_assignments{$device};
+
+       return $FALSE;
+}
+
+sub clearDevices {
+       my $group = shift;
+
+       if (!defined($GROUPS{$group})) {
+               print "WARNING: Failed to clear devices from group '$group', group does not exist.\n";
+               return $TRUE;
+       }
+
+       print "\t-> Clear virtual devices from group '$group'..\n";
+
+       if ($SCST->clearGroupDevices($group)) {
+               print "WARNING: Failed to clear devices from group '$group'.\n";
+               return $TRUE;
+       }
+
+       undef $ASSIGNMENTS{$group};
+
+       return $FALSE;
+}
+
+sub targets {
+       my %targets;
+       my %fcards;
+       my $root = new IO::Dir $_FC_CLASS_;
+
+       if ($root) {
+               while (my $entry = $root->read()) {
+                       next if (($entry eq '.') || ($entry eq '..'));
+
+                       my $io = new IO::File "$_FC_CLASS_/$entry/port_name", O_RDONLY;
+
+                       if ($io) {
+                               my $wwn = <$io>;
+                               chomp $wwn;
+                               close $io;
+
+                               $fcards{$entry} = $wwn;
+                       }
+               }
+       }
+
+       $root = new IO::Dir $_SCSI_CLASS_;
+
+       if ($root) {
+               while (my $entry = $root->read()) {
+                       next if (($entry eq '.') || ($entry eq '..'));
+
+                       my $io = new IO::File "$_SCSI_CLASS_/$entry/target_mode_enabled", O_RDONLY;
+
+                       if ($io) {
+                               my $enabled = <$io>;
+                               chomp $enabled;
+                               close $io;
+
+                               $targets{$entry}->{'path'} = "$_SCSI_CLASS_/$entry/target_mode_enabled";
+                               $targets{$entry}->{'enabled'} = $enabled;
+
+                               if ($fcards{$entry}) {
+                                       $targets{$fcards{$entry}}->{'enabled'} = $enabled;
+                                       $targets{$fcards{$entry}}->{'path'} =
+                                         "$_SCSI_CLASS_/$entry/target_mode_enabled";
+                                       $targets{$entry}->{'duplicate'} = $TRUE;
+                               } else {
+                                       $targets{$entry}->{'duplicate'} = $FALSE;
+                               }
+                       }
+               }
+       }
+
+       return \%targets;
+}
+
+sub targetEnabled {
+       my $target = shift;
+
+       return undef if (!defined($$TARGETS{$target}));
+       return $$TARGETS{$target}->{'enabled'};
+}
+
+sub enableTarget {
+       my $target = shift;
+       my $enable = shift;
+
+       return undef if (!defined($$TARGETS{$target}));
+
+       my $io = new IO::File $$TARGETS{$target}->{'path'}, O_WRONLY;
+       return $TRUE if (!$io);
+
+       print $enable ? "\t-> Enabling" : "\t-> Disabling";
+       print " target mode for SCST host '$target'.\n";
+
+       if ($_DEBUG_) {
+               print "DBG($$): ".$$TARGETS{$target}->{'path'}." -> $enable\n\n";
+       } else {
+               print $io $enable;
+       }
+
+       close $io;
+
+       $$TARGETS{unformatTarget($target)}->{'enabled'} = $enable;
+
+       return $FALSE;
+}
+
+sub readConfig {
+       my $confile = shift;
+       my %config;
+       my $section;
+       my $arg;
+
+       my $io = new IO::File $confile, O_RDONLY;
+
+       die("FATAL: Unable to open specified configuration file $confile: $!\n") if (!$io);
+
+       while (my $line = <$io>) {
+               ($line, undef) = split(/\#/, $line, 2);
+               $line = cleanupString($line);
+
+               if ($line =~ /^\[(.*)\]$/) {
+                       ($section, $arg) = split(/\s+/, $1, 2);
+               } elsif ($section && $arg && $line) {
+                       my($parameter, $value) = split(/\s+/, $line, 2);
+                       push @{$config{$section}->{$arg}->{$parameter}}, $value;
+               }
+       }
+
+       close $io;
+
+       return \%config;
+}
+
+sub cleanupString {
+       my $string = shift;
+
+       $string =~ s/^\s+//;
+       $string =~ s/\s+$//;
+
+       return $string;
+}
+
+sub formatTarget {
+       my $target = shift;
+
+       if ($target =~ /^0x/) {
+               $target =~ s/^0x//;
+               my($o1, $o2, $o3, $o4, $o5, $o6, $o7, $o8) = unpack("A2A2A2A2A2A2A2A2", $target);
+               $target = "$o1:$o2:$o3:$o4:$o5:$o6:$o7:$o8";
+       }
+
+       $target =~ tr/A-Z/a-z/;
+
+       return $target;
+}
+
+sub unformatTarget {
+       my $target = shift;
+
+       if ($target =~ /^.{2}\:.{2}\:.{2}\:.{2}\:.{2}\:.{2}\:.{2}\:.{2}/) {
+               $target =~ s/\://g;
+               $target = "0x$target";
+       }
+
+       $target =~ tr/A-Z/a-z/;
+
+       return $target;
+}
+
+# Hey! Stop that!
+sub commitSuicide {
+       print "\n\nAborting immediately.\n";
+       exit 1;
+}