From 5410bf4f3b3be2a2e2f154683cf2eef4f60c1977 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 14 Nov 2006 12:56:57 +0000 Subject: [PATCH] The initial commit of 0.9.5-pre1 version git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@33 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scstadmin/Changes | 17 + scstadmin/LICENSE | 340 +++++++ scstadmin/README | 53 ++ scstadmin/SCST/SCST.pm | 1011 +++++++++++++++++++++ scstadmin/examples/scst.conf | 91 ++ scstadmin/examples/scst_example.sql | 185 ++++ scstadmin/init.d/scst | 71 ++ scstadmin/scst.conf | 51 ++ scstadmin/scst_db/scst_db | 556 ++++++++++++ scstadmin/scst_db/scst_db.conf | 8 + scstadmin/scst_db/scst_schema.sql | 115 +++ scstadmin/scstadmin | 1270 +++++++++++++++++++++++++++ 12 files changed, 3768 insertions(+) create mode 100644 scstadmin/Changes create mode 100644 scstadmin/LICENSE create mode 100644 scstadmin/README create mode 100644 scstadmin/SCST/SCST.pm create mode 100644 scstadmin/examples/scst.conf create mode 100644 scstadmin/examples/scst_example.sql create mode 100755 scstadmin/init.d/scst create mode 100644 scstadmin/scst.conf create mode 100755 scstadmin/scst_db/scst_db create mode 100644 scstadmin/scst_db/scst_db.conf create mode 100644 scstadmin/scst_db/scst_schema.sql create mode 100755 scstadmin/scstadmin diff --git a/scstadmin/Changes b/scstadmin/Changes new file mode 100644 index 000000000..6bf7ba006 --- /dev/null +++ b/scstadmin/Changes @@ -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 index 000000000..08ddefd04 --- /dev/null +++ b/scstadmin/LICENSE @@ -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. + + + Copyright (C) + + 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. + + , 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 index 000000000..16a03348b --- /dev/null +++ b/scstadmin/README @@ -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 index 000000000..b64837d9c --- /dev/null +++ b/scstadmin/SCST/SCST.pm @@ -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:: + +For example: + +$SCST::SCST::MODISK_TYPE + +=cut diff --git a/scstadmin/examples/scst.conf b/scstadmin/examples/scst.conf new file mode 100644 index 000000000..c348095b2 --- /dev/null +++ b/scstadmin/examples/scst.conf @@ -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 index 000000000..b39732815 --- /dev/null +++ b/scstadmin/examples/scst_example.sql @@ -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 index 000000000..3e606259e --- /dev/null +++ b/scstadmin/init.d/scst @@ -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 index 000000000..eb71ff51f --- /dev/null +++ b/scstadmin/scst.conf @@ -0,0 +1,51 @@ +# SCST Configurator v0.2 + +# Handler devices +[HANDLER disk] +# DEVICE + +[HANDLER disk_fileio] +# DEVICE ,,, +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 + +[HANDLER changer] +# DEVICE + +[HANDLER modisk] +# DEVICE + +[HANDLER modisk_perf] + +[HANDLER tape] +# DEVICE + +[HANDLER tape_perf] + +# Security Group +[GROUP Test] +# USER +USER 50060b000001369d + +# Device Assignments +[ASSIGNMENT Test] +# DEVICE , +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 index 000000000..6b3c6f9d5 --- /dev/null +++ b/scstadmin/scst_db/scst_db @@ -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 : 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 index 000000000..9569e9c59 --- /dev/null +++ b/scstadmin/scst_db/scst_db.conf @@ -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 index 000000000..031bf091b --- /dev/null +++ b/scstadmin/scst_db/scst_schema.sql @@ -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 index 000000000..e4e1f9164 --- /dev/null +++ b/scstadmin/scstadmin @@ -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 : Configure SCST given the specified configuration file. + -ClearConfig : Clear all SCST configuration. + -WriteConfig : Writes the current configuration out to the specified file. + -checkConfig : Checks the saved configuration in the specified file. + -sessions : List current initiator sessions. + +Target Driver Operations + -enable : Enable target mode for driver at specified WWN or host. + -disable : Disable target mode for driver at specified WWN or host. + +Devices Operations + -adddev : Adds a device to a handler. + -handler + -path + -options + -blocksize + -RemoveDev : Remove a device from a handler. + -handler + +Users Operations + -adduser : Adds a user to a security group. + -group + -RemoveUser : Delete a user from a security group. + -group + -ClearUsers : Clear all users from a given security group. + -group + +Groups Operations + -addgroup : Add a given group to available security groups. + -RemoveGroup : Remove a give group from available security groups. + +Assignments Operations + -assigndev : Assign a given device to a security group. + -group + -lun + -ReleaseDev : Remove a given device from a security group. + -group + -ClearDevs : Clear all device assignments for a security 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; +}