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