blob: df34d005244ec00e10b92a87a67664fc97ebde91 [file] [log] [blame]
# -*- mode: perl; perl-indent-level: 8; indent-tabs-mode: t -*-
package SCST::SCST;
# Author: Mark R. Buechler
# License: GPLv2
# Copyright (c) 2005-2011 Mark R. Buechler
# Copyright (c) 2011-2019 Bart Van Assche <bvanassche@acm.org>.
use strict;
use warnings;
use 5.005;
use Fcntl ':mode';
use File::Spec;
use IO::Handle;
use IO::File;
use Carp qw(cluck);
use POSIX;
use constant {
TRUE => 1,
FALSE => 0,
SCST_ROOT => '/sys/kernel/scst_tgt',
# Root-level
SCST_SGV => 'sgv',
SCST_HANDLERS => 'handlers',
SCST_DEVICES => 'devices',
SCST_TARGETS => 'targets',
SCST_DEV_GROUPS => 'device_groups',
SCST_QUEUE_RES => 'last_sysfs_mgmt_res',
# Device group specific
SCST_DG_DEVICES => 'devices',
SCST_DG_TGROUPS => 'target_groups',
# Target specific
SCST_GROUPS => 'ini_groups',
SCST_INITIATORS => 'initiators',
SCST_SESSIONS => 'sessions',
SCST_LUNS => 'luns',
# Files
SCST_MGMT_IO => 'mgmt',
SCST_VERSION_IO => 'version',
SCST_TRACE_IO => 'trace_level',
SCST_RESYNC_IO => 'resync_size',
SCST_T10_IO => 't10_dev_id',
# Module return codes
SCST_C_FATAL_ERROR => 2,
SCST_C_BAD_ATTRIBUTES => 7,
SCST_C_ATTRIBUTE_STATIC => 8,
SCST_C_SETATTR_FAIL => 9,
SCST_C_HND_NO_HANDLER => 10,
SCST_C_HND_BAD_ATTRIBUTES => 17,
SCST_C_HND_ATTRIBUTE_STATIC => 18,
SCST_C_HND_SETATTR_FAIL => 19,
SCST_C_DEV_NO_DEVICE => 20,
SCST_C_DEV_EXISTS => 21,
SCST_C_DEV_OPEN_FAIL => 22,
SCST_C_DEV_CLOSE_FAIL => 23,
SCST_C_DEV_BAD_ATTRIBUTES => 27,
SCST_C_DEV_ATTRIBUTE_STATIC => 28,
SCST_C_DEV_SETATTR_FAIL => 29,
SCST_C_DRV_NO_DRIVER => 30,
SCST_C_DRV_NOTVIRT => 31,
SCST_C_DRV_SETATTR_FAIL => 34,
SCST_C_DRV_ADDATTR_FAIL => 35,
SCST_C_DRV_REMATTR_FAIL => 36,
SCST_C_DRV_BAD_ATTRIBUTES => 37,
SCST_C_DRV_ATTRIBUTE_STATIC => 38,
SCST_C_DRV_SETATTR_FAIL => 39,
SCST_C_TGT_NO_TARGET => 40,
SCST_C_TGT_EXISTS => 41,
SCST_C_TGT_ADD_FAIL => 42,
SCST_C_TGT_REM_FAIL => 43,
SCST_C_TGT_SETATTR_FAIL => 44,
SCST_C_TGT_ADDATTR_FAIL => 45,
SCST_C_TGT_REMATTR_FAIL => 46,
SCST_C_TGT_NO_LUN => 47,
SCST_C_TGT_ADD_LUN_FAIL => 48,
SCST_C_TGT_LUN_EXISTS => 49,
SCST_C_TGT_BAD_ATTRIBUTES => 50,
SCST_C_TGT_ATTRIBUTE_STATIC => 51,
SCST_C_TGT_SETATTR_FAIL => 52,
SCST_C_TGT_CLR_LUN_FAIL => 53,
SCST_C_TGT_BUSY => 54,
SCST_C_GRP_NO_GROUP => 60,
SCST_C_GRP_EXISTS => 61,
SCST_C_GRP_ADD_FAIL => 62,
SCST_C_GRP_REM_FAIL => 63,
SCST_C_GRP_NO_LUN => 70,
SCST_C_GRP_LUN_EXISTS => 71,
SCST_C_GRP_ADD_LUN_FAIL => 72,
SCST_C_GRP_REM_LUN_FAIL => 73,
SCST_C_GRP_CLR_LUN_FAIL => 75,
SCST_C_GRP_BAD_ATTRIBUTES => 77,
SCST_C_GRP_ATTRIBUTE_STATIC => 78,
SCST_C_GRP_SETATTR_FAIL => 79,
SCST_C_GRP_NO_INI => 80,
SCST_C_GRP_INI_EXISTS => 81,
SCST_C_GRP_ADD_INI_FAIL => 82,
SCST_C_GRP_REM_INI_FAIL => 83,
SCST_C_GRP_MOV_INI_FAIL => 84,
SCST_C_GRP_CLR_INI_FAIL => 85,
SCST_C_LUN_DEV_EXISTS => 91,
SCST_C_LUN_RPL_DEV_FAIL => 96,
SCST_C_LUN_BAD_ATTRIBUTES => 97,
SCST_C_LUN_ATTRIBUTE_STATIC => 98,
SCST_C_LUN_SETATTR_FAIL => 99,
SCST_C_INI_BAD_ATTRIBUTES => 100,
SCST_C_INI_ATTRIBUTE_STATIC => 101,
SCST_C_INI_SETATTR_FAIL => 102,
SCST_C_NO_SESSION => 110,
SCST_C_SESSION_CLOSE_FAIL => 111,
SCST_C_DEV_GRP_NO_GROUP => 120,
SCST_C_DEV_GRP_EXISTS => 121,
SCST_C_DEV_GRP_ADD_FAIL => 122,
SCST_C_DEV_GRP_REM_FAIL => 123,
SCST_C_ALUA_BAD_ATTRIBUTES => 125,
SCST_C_ALUA_ATTRIBUTE_STATIC => 126,
SCST_C_ALUA_SETATTR_FAIL => 127,
SCST_C_DGRP_ADD_DEV_FAIL => 130,
SCST_C_DGRP_REM_DEV_FAIL => 131,
SCST_C_DGRP_NO_DEVICE => 132,
SCST_C_DGRP_DEVICE_EXISTS => 133,
SCST_C_DGRP_ADD_GRP_FAIL => 134,
SCST_C_DGRP_REM_GRP_FAIL => 135,
SCST_C_DGRP_NO_GROUP => 136,
SCST_C_DGRP_GROUP_EXISTS => 137,
SCST_C_DGRP_DEVICE_OTHER => 138,
SCST_C_DGRP_BAD_ATTRIBUTES => 140,
SCST_C_DGRP_ATTRIBUTE_STATIC => 141,
SCST_C_DGRP_SETATTR_FAIL => 142,
SCST_C_TGRP_BAD_ATTRIBUTES => 150,
SCST_C_TGRP_ATTRIBUTE_STATIC => 151,
SCST_C_TGRP_SETATTR_FAIL => 152,
SCST_C_TGRP_ADD_TGT_FAIL => 160,
SCST_C_TGRP_REM_TGT_FAIL => 161,
SCST_C_TGRP_NO_TGT => 162,
SCST_C_TGRP_TGT_EXISTS => 163,
SCST_C_TGRP_TGT_BAD_ATTR => 170,
SCST_C_TGRP_TGT_ATTR_STATIC => 171,
SCST_C_TGRP_TGT_SETATTR_FAIL => 172,
};
my %VERBOSE_ERROR = (
(SCST_C_FATAL_ERROR) => 'A fatal error occurred. See "dmesg" for more information.',
(SCST_C_BAD_ATTRIBUTES) => 'Bad attributes given for SCST.',
(SCST_C_ATTRIBUTE_STATIC) => 'SCST attribute specified is static',
(SCST_C_SETATTR_FAIL) => 'Failed to set a SCST attribute. See "dmesg" for more information.',
(SCST_C_HND_NO_HANDLER) => 'No such handler exists.',
(SCST_C_HND_BAD_ATTRIBUTES) => 'Bad attributes given for handler.',
(SCST_C_HND_ATTRIBUTE_STATIC) => 'Handler attribute given is static.',
(SCST_C_HND_SETATTR_FAIL) => 'Failed to set handler attribute. See "dmesg" for more information.',
(SCST_C_DEV_NO_DEVICE) => 'No such device exists.',
(SCST_C_DEV_EXISTS) => 'Device already exists.',
(SCST_C_DEV_OPEN_FAIL) => 'Failed to open device. See "dmesg" for more information.',
(SCST_C_DEV_CLOSE_FAIL) => 'Failed to close device. See "dmesg" for more information.',
(SCST_C_DEV_BAD_ATTRIBUTES) => 'Bad attributes given for device.',
(SCST_C_DEV_ATTRIBUTE_STATIC) => 'Device attribute specified is static.',
(SCST_C_DEV_SETATTR_FAIL) => 'Failed to set device attribute. See "dmesg" for more information.',
(SCST_C_DRV_NO_DRIVER) => 'No such driver exists.',
(SCST_C_DRV_NOTVIRT) => 'Driver is incapable of dynamically adding/removing targets or attributes.',
(SCST_C_DRV_ADDATTR_FAIL) => 'Failed to add driver dynamic attribute. See "dmesg" for more information.',
(SCST_C_DRV_REMATTR_FAIL) => 'Failed to remove driver dymanic attribute. See "dmesg" for more information.',
(SCST_C_DRV_BAD_ATTRIBUTES) => 'Bad attributes given for driver.',
(SCST_C_DRV_ATTRIBUTE_STATIC) => 'Driver attribute specified is static.',
(SCST_C_DRV_SETATTR_FAIL) => 'Failed to set driver attribute. See "dmesg" for more information.',
(SCST_C_TGT_NO_TARGET) => 'No such target exists.',
(SCST_C_TGT_EXISTS) => 'Target already exists.',
(SCST_C_TGT_ADD_FAIL) => 'Failed to add target. See "dmesg" for more information.',
(SCST_C_TGT_REM_FAIL) => 'Failed to remove target. See "dmesg" for more information.',
(SCST_C_TGT_SETATTR_FAIL) => 'Failed to set target attribute. See "dmesg" for more information.',
(SCST_C_TGT_ADDATTR_FAIL) => 'Failed to add target dynamic attribute. See "dmesg" for more information.',
(SCST_C_TGT_REMATTR_FAIL) => 'Failed to remove target dynamic attribute. See "dmesg" for more information.',
(SCST_C_TGT_NO_LUN) => 'No such LUN exists.',
(SCST_C_TGT_ADD_LUN_FAIL) => 'Failed to add LUN to target. See "dmesg" for more information.',
(SCST_C_TGT_LUN_EXISTS) => 'LUN already exists.',
(SCST_C_TGT_BAD_ATTRIBUTES) => 'Bad attributes given for target.',
(SCST_C_TGT_ATTRIBUTE_STATIC) => 'Target attribute specified is static.',
(SCST_C_TGT_SETATTR_FAIL) => 'Failed to set target attribute. See "dmesg" for more information.',
(SCST_C_TGT_CLR_LUN_FAIL) => 'Failed to clear LUNs from target. See "dmesg" for more information.',
(SCST_C_TGT_BUSY) => 'Failed to remove target - target has active sessions. See "dmesg" for more information.',
(SCST_C_GRP_NO_GROUP) => 'No such group exists.',
(SCST_C_GRP_EXISTS) => 'Group already exists.',
(SCST_C_GRP_ADD_FAIL) => 'Failed to add group. See "dmesg" for more information.',
(SCST_C_GRP_REM_FAIL) => 'Failed to remove group. See "dmesg" for more information.',
(SCST_C_GRP_NO_LUN) => 'No such LUN exists.',
(SCST_C_GRP_LUN_EXISTS) => 'LUN already exists.',
(SCST_C_GRP_ADD_LUN_FAIL) => 'Failed to add LUN to group. See "dmesg" for more information.',
(SCST_C_GRP_REM_LUN_FAIL) => 'Failed to remove LUN. See "dmesg" for more information.',
(SCST_C_GRP_CLR_LUN_FAIL) => 'Failed to clear LUNs from group. See "dmesg" for more information.',
(SCST_C_GRP_BAD_ATTRIBUTES) => 'Bad attributes given for group.',
(SCST_C_GRP_ATTRIBUTE_STATIC) => 'Group attribute specified is static.',
(SCST_C_GRP_SETATTR_FAIL) => 'Failed to set group attribute. See "dmesg" for more information.',
(SCST_C_GRP_NO_INI) => 'No such initiator exists.',
(SCST_C_GRP_INI_EXISTS) => 'Initiator already exists.',
(SCST_C_GRP_ADD_INI_FAIL) => 'Failed to add initiator. See "dmesg" for more information.',
(SCST_C_GRP_REM_INI_FAIL) => 'Failed to remove initiator. See "dmesg" for more information.',
(SCST_C_GRP_MOV_INI_FAIL) => 'Failed to move initiator. See "dmesg" for more information.',
(SCST_C_GRP_CLR_INI_FAIL) => 'Failed to clear initiators. See "dmesg" for more information.',
(SCST_C_LUN_DEV_EXISTS) => 'Device already exists for LUN.',
(SCST_C_LUN_RPL_DEV_FAIL) => 'Failed to replace device for LUN. See "dmesg" for more information.',
(SCST_C_LUN_BAD_ATTRIBUTES) => 'Bad attributes for LUN.',
(SCST_C_LUN_ATTRIBUTE_STATIC) => 'LUN attribute specified is static.',
(SCST_C_LUN_SETATTR_FAIL) => 'Failed to set LUN attribute. See "dmesg" for more information.',
(SCST_C_INI_BAD_ATTRIBUTES) => 'Bad attributes for initiator.',
(SCST_C_INI_ATTRIBUTE_STATIC) => 'Initiator attribute specified is static.',
(SCST_C_INI_SETATTR_FAIL) => 'Failed to set initiator attribute. See "dmesg" for more information.',
(SCST_C_NO_SESSION) => 'Session not found for driver/target.',
(SCST_C_SESSION_CLOSE_FAIL) => 'Failed to close session.',
(SCST_C_DEV_GRP_NO_GROUP) => 'No such device group exists.',
(SCST_C_DEV_GRP_EXISTS) => 'Device group already exists.',
(SCST_C_DEV_GRP_ADD_FAIL) => 'Failed to add device group. See "dmesg" for more information.',
(SCST_C_DEV_GRP_REM_FAIL) => 'Failed to remove device group. See "dmesg" for more information.',
(SCST_C_DGRP_ADD_DEV_FAIL) => 'Failed to add device to device group. See "dmesg" for more information.',
(SCST_C_DGRP_REM_DEV_FAIL) => 'Failed to remove device from device group. See "dmesg" for more information.',
(SCST_C_DGRP_NO_DEVICE) => 'No such device in device group.',
(SCST_C_DGRP_DEVICE_EXISTS) => 'Device already exists within device group.',
(SCST_C_DGRP_ADD_GRP_FAIL) => 'Failed to add target group to device group. See "dmesg" for more information.',
(SCST_C_DGRP_REM_GRP_FAIL) => 'Failed to remove target group from device group. See "dmesg" for more information.',
(SCST_C_DGRP_NO_GROUP) => 'No such target group exists within device group.',
(SCST_C_DGRP_GROUP_EXISTS) => 'Target group already exists within device group.',
(SCST_C_DGRP_DEVICE_OTHER) => 'Device is already assigned to another device group.',
(SCST_C_DGRP_BAD_ATTRIBUTES) => 'Bad attributes for device group.',
(SCST_C_DGRP_ATTRIBUTE_STATIC) => 'Device group attribute specified is static.',
(SCST_C_DGRP_SETATTR_FAIL) => 'Failed to set device group attribute. See "dmesg" for more information.',
(SCST_C_TGRP_BAD_ATTRIBUTES) => 'Bad attributes for target group.',
(SCST_C_TGRP_ATTRIBUTE_STATIC) => 'Target group attribute specified is static.',
(SCST_C_TGRP_SETATTR_FAIL) => 'Failed to set target group attribute. See "dmesg" for more information.',
(SCST_C_TGRP_ADD_TGT_FAIL) => 'Failed to add target to target group.',
(SCST_C_TGRP_REM_TGT_FAIL) => 'Failed to remove target from target group.',
(SCST_C_TGRP_NO_TGT) => 'No such target exists within target group.',
(SCST_C_TGRP_TGT_EXISTS) => 'Target already exists within target group.',
(SCST_C_TGRP_TGT_BAD_ATTR) => 'Bad attributes for target group target.',
(SCST_C_TGRP_TGT_ATTR_STATIC) => 'Target group target attribute specified is static.',
(SCST_C_TGRP_TGT_SETATTR_FAIL) => 'Failed to set target group target attribute. See "dmesg" for more information.',
);
use vars qw(@ISA @EXPORT $VERSION);
use vars qw($TGT_TYPE_HARDWARE $TGT_TYPE_VIRTUAL);
$VERSION = '1.0.0';
$TGT_TYPE_HARDWARE = 1;
$TGT_TYPE_VIRTUAL = 2;
my $TIMEOUT = 300; # Command execution timeout
my $_SCST_MIN_MAJOR_ = 2;
my $_SCST_MIN_MINOR_ = 0;
my $_SCST_MIN_RELEASE_ = 0;
sub SCST_ROOT_DIR {
return SCST_ROOT;
}
sub SCST_SGV_DIR {
return SCST_ROOT . '/sgv';
}
sub SCST_QUEUE_RES_PATH {
return SCST_ROOT . '/' . SCST_QUEUE_RES;
}
# Device handlers.
sub SCST_HANDLERS_DIR {
return SCST_ROOT . '/' . SCST_HANDLERS;
}
# Device instances.
sub SCST_DEVICES_DIR {
return SCST_ROOT . '/' . SCST_DEVICES;
}
# Target drivers.
sub SCST_TARGETS_DIR {
return SCST_ROOT . '/' . SCST_TARGETS;
}
# ALUA Device groups.
sub SCST_DEV_GROUP_DIR {
return SCST_ROOT . '/' . SCST_DEV_GROUPS;
}
sub new {
my $this = shift;
my $debug = shift;
my $badVersion = 1;
my $class = ref($this) || $this;
my $self = {};
bless($self, $class);
$self->{'debug'} = $debug;
my $scstVersion = $self->scstVersion();
die("Failed to obtain SCST version information. Are the SCST modules loaded?\n")
if (!defined($scstVersion));
($scstVersion, undef) = split(/\-/, $scstVersion);
my($major, $minor, $release) = split(/\./, $scstVersion, 3);
($release, undef) = split(/\-/, $release) if ($release =~ /\-/);
$badVersion = 0 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_)));
die("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;
}
# Returns 1 if and only if the owner of a file is not allowed to write to a
# file.
sub readOnly {
my ($path) = @_;
my $mode = (stat($path))[2];
if (!defined($mode)) {
cluck("invalid path $path");
return undef;
}
return ($mode & S_IWUSR) == 0;
}
sub scstVersion {
my $self = shift;
my ($attributes, $errorString) = $self->scstAttributes();
return undef if (!defined($attributes));
return $$attributes{'version'}->{'value'};
}
sub scstAttributes {
my $self = shift;
my %attributes = ( );
my $pHandle = new IO::Handle;
my $_path = SCST_ROOT_DIR();
if (!(opendir $pHandle, $_path)) {
return (undef, "scstAttributes(): Unable to read directory '$_path': $!");
}
foreach my $attribute (readdir($pHandle)) {
next if ($attribute eq '.' || $attribute eq '..' ||
$attribute eq SCST_MGMT_IO);
my $pPath = make_path(SCST_ROOT_DIR(), $attribute);
my $mode = (stat($pPath))[2];
if (-d $pPath) {
# Skip directories
} else {
if (!(($mode & S_IRUSR) >> 6)) {
$attributes{$attribute}->{'static'} = FALSE;
$attributes{$attribute}->{'value'} = undef;
} else {
my $is_static;
if (($mode & S_IWUSR) >> 6) {
$is_static = FALSE;
} else {
$is_static = TRUE;
}
my $io = new IO::File $pPath, O_RDONLY;
if (!$io) {
return (undef, "scsiAttributes(): Unable to read ".
"scst attribute '$attribute': $!");
}
my $value = <$io>;
$value = "" if (!defined($value));
chomp $value;
my $is_key = <$io>;
$is_key = $is_key && $is_key =~ /\[key\]/;
my $key = 0;
if ($is_key) {
if ($attribute =~ /^([^\d]+)(\d+)$/) {
$attribute = $1;
$key = $2;
}
}
if ($attribute eq SCST_TRACE_IO) {
$attributes{$attribute}->{'value'} = $value;
my @possible;
$value = '';
my $start = FALSE;
while (my $line = <$io>) {
$start = TRUE if ($line !~ /\[/);
$value .= $line if ($start);
}
$value =~ s/\n//g;
if ($value =~ /\[(.*)\]/) {
$value = $1;
foreach my $t (split(/\,/, $value)) {
$t =~ s/^\s+//; $t =~ s/\s+$//;
push @possible, $t;
}
}
$attributes{$attribute}->{'set'} = \@possible;
} else {
if ($is_key) {
$attributes{$attribute}->{'keys'}->{$key}->{'value'} = $value;
} else {
$attributes{$attribute}->{'value'} = $value;
}
}
close $io;
$attributes{$attribute}->{'static'} = $is_static;
}
}
}
close $pHandle;
return (\%attributes, undef);
}
# Older Perl versions complain if the argument of length() is undefined. Hence
# this function that checks whether its arguments are defined and not empty.
sub valid {
for my $arg (@_) {
return FALSE if (!defined($arg) || $arg eq "");
}
return TRUE;
}
# Convert e.g. EINVAL into "EINVAL".
sub my_strerror {
my ($errorcode) = @_;
return $errorcode if (!defined($errorcode));
for my $errstr (keys(%!)) {
my $err = eval($errstr);
return $errstr if (defined($err) and $err == $errorcode);
}
return $errorcode;
}
sub setAttrFailed {
my ($path, $bytes, $no_such_attr, $is_static, $failed) = @_;
print STDERR "(" . my_strerror(-$bytes) . ") ";
return $no_such_attr if !(-f $path);
return $is_static if readOnly($path);
return $failed;
}
sub setScstAttribute {
my $self = shift;
my $attribute = shift;
my $value = shift;
return TRUE if (!valid($attribute) || !defined($value));
# There is a space between the unary minus sign and ENOENT because
# without that space older Perl versions report the following warning:
# Ambiguous use of -ENOENT resolved as -&ENOENT() at
# /usr/share/perl5/vendor_perl/SCST/SCST.pm line 497.
my $bytes = - ENOENT;
my $path = make_path(SCST_ROOT_DIR(), $attribute);
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $attribute = $value\n";
} else {
$value .= "\n";
$bytes = _syswrite($io, $value, length($value));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
return setAttrFailed($path, $bytes, SCST_C_BAD_ATTRIBUTES,
SCST_C_ATTRIBUTE_STATIC, SCST_C_SETATTR_FAIL);
}
sub drivers {
my $self = shift;
my $dHandle = new IO::Handle;
my $_path = SCST_TARGETS_DIR();
my @drivers;
if (opendir($dHandle, $_path)) {
foreach my $driver (readdir($dHandle)) {
next if ($driver eq '.' || $driver eq '..');
if (-d make_path(SCST_TARGETS_DIR(), $driver)) {
push @drivers, $driver;
}
}
@drivers = sort(@drivers);
close $dHandle;
} else {
return (undef, "drivers(): Unable to read directory '$_path': $!");
}
return (\@drivers, undef);
}
sub targets {
my $self = shift;
my $driver = shift;
return (undef, "Too few arguments") if (!valid($driver));
my $tHandle = new IO::Handle;
my $_path = make_path(SCST_TARGETS_DIR(), $driver);
if (opendir $tHandle, $_path) {
my @targets;
foreach my $target (readdir($tHandle)) {
next if ($target eq '.' || $target eq '..' ||
$target eq 'module');
if (-d make_path(SCST_TARGETS_DIR(), $driver,
$target)) {
push @targets, $target;
}
}
close $tHandle;
@targets = sort(@targets);
return (\@targets, undef);
}
my $errorString;
if ($self->driverExists($driver) != TRUE) {
$errorString = "targets(): Driver '$driver' is not available";
} else {
$errorString = "targets(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
sub groups {
my $self = shift;
my $driver = shift;
my $target = shift;
return (undef, "Too few arguments") if (!valid($driver, $target));
my $gHandle = new IO::Handle;
my $_path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_GROUPS);
if (opendir $gHandle, $_path) {
my @groups;
foreach my $group (readdir($gHandle)) {
next if ($group eq '.' || $group eq '..');
if (-d make_path(SCST_TARGETS_DIR(), $driver,
$target, SCST_GROUPS, $group)) {
push @groups, $group;
}
}
close $gHandle;
@groups = sort(@groups);
return (\@groups, undef);
}
my $errorString;
if ($self->driverExists($driver) != TRUE) {
$errorString = "groups(): Driver '$driver' is not available";
} elsif ($self->targetExists($driver, $target) != TRUE) {
$errorString = "groups(): Target '$target' is not available";
} else {
$errorString = "groups(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
sub initiators {
my $self = shift;
my $driver = shift;
my $target = shift;
my $group = shift;
my @initiators;
my $errorString;
return (undef, "Too few arguments")
if (!valid($driver, $target, $group));
my $iHandle = new IO::Handle;
my $_path = make_path(SCST_TARGETS_DIR(), $driver, $target, SCST_GROUPS,
$group, SCST_INITIATORS);
if (!(opendir $iHandle, $_path)) {
if ($self->driverExists($driver) != TRUE) {
$errorString = "initiators(): Driver '$driver' is not available";
} elsif ($self->targetExists($driver, $target) != TRUE) {
$errorString = "initiators(): Target '$target' is not available";
} elsif ($self->groupExists($driver, $target, $group) != TRUE) {
$errorString = "initiators(): Group '$group' does not exist";
} else {
$errorString = "initiators(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
foreach my $initiator (readdir($iHandle)) {
next if (($initiator eq '.') || ($initiator eq '..'));
next if ($initiator eq SCST_MGMT_IO);
push @initiators, $initiator;
}
close $iHandle;
return (\@initiators, undef);
}
sub luns {
my $self = shift;
my $driver = shift;
my $target = shift;
my $group = shift;
my $errorString;
return (undef, "Too few arguments") if (!valid($driver, $target));
my $_path;
if (valid($group)) {
$_path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_GROUPS, $group, SCST_LUNS);
} else {
$_path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_LUNS);
}
my $lHandle = new IO::Handle;
if (!(opendir $lHandle, $_path)) {
if (valid($group) &&
$self->groupExists($driver, $target, $group) != TRUE) {
$errorString = "initiators(): Group '$group' does not exist";
} elsif ($self->driverExists($driver) != TRUE) {
$errorString = "luns(): Driver '$driver' is not available";
} elsif ($self->targetExists($driver, $target) != TRUE) {
$errorString = "luns(): Target '$target' is not available";
} else {
$errorString = "luns(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
my %luns;
foreach my $lun (readdir($lHandle)) {
next if (($lun eq '.') || ($lun eq '..'));
my $lPath = make_path($_path, $lun);
if (-d $lPath) {
my $_lHandle = new IO::Handle;
if (!(opendir $_lHandle, $lPath)) {
return (undef, "luns(): Unable to read directory '$lPath': $!");
}
foreach my $attribute (readdir($_lHandle)) {
my $pPath = make_path($lPath, $attribute);
if (-l $pPath) {
my $linked = readlink($pPath);
$linked =~ s/.*\///;
$luns{$lun} = $linked;
}
}
close $_lHandle;
}
}
close $lHandle;
return \%luns;
}
sub aluaAttributes {
my $self = shift;
my %attributes = ( );
my $pHandle = new IO::Handle;
my $_path = SCST_DEV_GROUP_DIR();
if (!(opendir $pHandle, $_path)) {
return (undef, "deviceGroupsAttributes(): Unable to read directory '$_path': $!");
}
foreach my $attribute (readdir($pHandle)) {
next if ($attribute eq '.' || $attribute eq '..' ||
$attribute eq SCST_MGMT_IO || $attribute eq 'uevent');
my $pPath = make_path(SCST_DEV_GROUP_DIR(), $attribute);
my $mode = (stat($pPath))[2];
if (!($mode & S_IRUSR)) {
$attributes{$attribute}->{'static'} = FALSE;
$attributes{$attribute}->{'value'} = undef;
} else {
my $is_static = !($mode & S_IWUSR);
my $io = new IO::File $pPath, O_RDONLY;
if (!$io) {
return (undef, "aluaAttributes(): Unable to read device attribute '$attribute': $!");
}
my $value = <$io>;
$value = "" if (!defined($value));
chomp $value;
my $second_line = <$io>;
$second_line = "" if (!defined($second_line));
if ($second_line =~ /\[key\]/) {
my $key = 0;
if ($attribute =~ /^([^\d]+)(\d+)$/) {
$attribute = $1;
$key = $2;
}
$attributes{$attribute}->{'keys'}->{$key}->{'value'} = $value;
} else {
$attributes{$attribute}->{'value'} = $value;
}
close $io;
$attributes{$attribute}->{'static'} = $is_static;
}
}
close $pHandle;
return (\%attributes, undef);
}
sub deviceGroups {
my $self = shift;
my @groups;
my $dHandle = new IO::Handle;
my $_path = SCST_DEV_GROUP_DIR();
if (!(opendir $dHandle, $_path)) {
return (undef, "deviceGroups(): Unable to read directory '$_path': $!");
}
foreach my $group (readdir($dHandle)) {
next if (($group eq '.') || ($group eq '..'));
if (-d make_path(SCST_DEV_GROUP_DIR(), $group)) {
push @groups, $group;
}
}
close $dHandle;
@groups = sort(@groups);
return (\@groups, undef);
}
sub deviceGroupDevices {
my $self = shift;
my $group = shift;
my @devices;
my $errorString;
return (undef, "Too few arguments") if (!valid($group));
my $dHandle = new IO::Handle;
my $_path = make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_DEVICES);
if (!(opendir $dHandle, $_path)) {
if ($self->deviceGroupExists($group) != TRUE) {
$errorString = "deviceGroupDevices(): Device group '$group' does not exist";
} else {
$errorString = "deviceGroupDevices(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
foreach my $device (readdir($dHandle)) {
next if (($device eq '.') || ($device eq '..'));
if (-d make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_DEVICES, $device)) {
push @devices, $device;
}
}
close $dHandle;
return (\@devices, undef);
}
sub targetGroups {
my $self = shift;
my $group = shift;
my @tgroups;
my $errorString;
return (undef, "Too few arguments") if (!valid($group));
my $dHandle = new IO::Handle;
my $_path = make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_TGROUPS);
if (!(opendir $dHandle, $_path)) {
if ($self->deviceGroupExists($group) != TRUE) {
$errorString = "targetGroups(): Device group '$group' does not exist";
} else {
$errorString = "targetGroups(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
foreach my $tgroup (readdir($dHandle)) {
next if (($tgroup eq '.') || ($tgroup eq '..'));
if (-d make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_TGROUPS, $tgroup)) {
push @tgroups, $tgroup;
}
}
close $dHandle;
return (\@tgroups, undef);
}
sub targetGroupTargets {
my $self = shift;
my $group = shift;
my $tgroup = shift;
my @targets;
my $errorString;
return (undef, "Too few arguments") if (!valid($group, $tgroup));
my $dHandle = new IO::Handle;
my $_path = make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_TGROUPS, $tgroup);
if (!(opendir $dHandle, $_path)) {
if ($self->deviceGroupExists($group) != TRUE) {
$errorString = "targetGroupTargets(): Device group '$group' does not exist";
} elsif ($self->targetGroupExists($group, $tgroup) != TRUE) {
$errorString = "targetGroupTargets(): Target group '$tgroup' does not exist";
} else {
$errorString = "targetGroupTargets(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
foreach my $tgt (readdir($dHandle)) {
next if (($tgt eq '.') || ($tgt eq '..'));
if (-d make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_TGROUPS, $tgroup, $tgt)) {
push @targets, $tgt;
}
}
close $dHandle;
@targets = sort(@targets);
return (\@targets, undef);
}
sub driverExists {
my $self = shift;
my $driver = shift;
my $dHandle = new IO::Handle;
my $result;
$result = valid($driver) &&
opendir($dHandle, make_path(SCST_TARGETS_DIR(), $driver));
close $dHandle if ($result);
return $result ? TRUE : FALSE;
}
sub driverDynamicAttributes {
my $self = shift;
my $driver = shift;
my %attributes = ( );
my $available;
my $errorString;
return (undef, "Too few arguments") if (!valid($driver));
my $io = new IO::File make_path(SCST_TARGETS_DIR(), $driver,
SCST_MGMT_IO), O_RDONLY;
if (!$io) {
if ($self->driverExists($driver) != TRUE) {
$errorString = "driverDynamicAttributes(): Driver '$driver' ".
"is not available";
} else {
$errorString = "driverDynamicAttributes(): Unable to open mgmt ".
"interface for driver '$driver': $!";
}
return (undef, $errorString);
}
while (my $in = <$io>) {
if ($in =~ /^The following target driver attributes available\:/) {
(undef, $available) = split(/\:/, $in, 2);
$available =~ s/\.$//;
}
}
close $io;
if ($available) {
foreach my $attribute (split(/\,/, $available)) {
$attribute =~ s/^\s+//;
$attribute =~ s/\s+$//;
$attributes{$attribute} = '';
}
}
return (\%attributes, undef);
}
sub checkDriverDynamicAttributes {
my $self = shift;
my $driver = shift;
my $check = shift;
return TRUE if (!valid($driver));
my ($available, $errorString) = $self->driverDynamicAttributes($driver);
if (!defined($available)) {
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if (!$rc > 1);
return SCST_C_FATAL_ERROR;
}
return FALSE if (!defined($check));
if (ref($check) eq 'HASH') {
foreach my $attribute (keys %{$check}) {
if (!defined($$available{$attribute})) {
return TRUE;
}
}
} else {
if (!defined($$available{$check})) {
return TRUE;
}
}
return FALSE;
}
sub addDriverDynamicAttribute {
my $self = shift;
my $driver = shift;
my $attribute = shift;
my $value = shift;
return SCST_C_DRV_ADDATTR_FAIL if (!valid($driver, $attribute) ||
!defined($value));
my ($path, $cmd);
$path = make_path(SCST_TARGETS_DIR(), $driver, SCST_MGMT_IO);
$cmd .= "add_attribute $attribute $value";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
return SCST_C_DRV_NOTVIRT if (!$self->driverIsVirtualCapable($driver));
$rc = $self->checkDriverDynamicAttributes($driver, $attribute);
return SCST_C_DRV_BAD_ATTRIBUTES if ($rc == 1);
return $rc if ($rc > 1);
return SCST_C_DRV_ADDATTR_FAIL;
}
sub removeDriverDynamicAttribute {
my $self = shift;
my $driver = shift;
my $attribute = shift;
my $value = shift;
return SCST_C_DRV_REMATTR_FAIL if (!valid($driver, $attribute) ||
!defined($value));
my ($path, $cmd);
$path = make_path(SCST_TARGETS_DIR(), $driver, SCST_MGMT_IO);
$cmd .= "del_attribute $attribute $value";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
return SCST_C_DRV_NOTVIRT if (!$self->driverIsVirtualCapable($driver));
$rc = $self->checkDriverDynamicAttributes($driver, $attribute);
return SCST_C_DRV_BAD_ATTRIBUTES if ($rc == 1);
return $rc if ($rc > 1);
return SCST_C_DRV_REMATTR_FAIL;
}
sub targetExists {
my $self = shift;
my $driver = shift;
my $target = shift;
return valid($driver, $target) &&
$target ne '.' && $target ne '..' && $target ne 'module' &&
(-d make_path(SCST_TARGETS_DIR(), $driver, $target)) ? TRUE : FALSE;
}
sub driverIsVirtualCapable {
my $self = shift;
my $driver = shift;
my $path = make_path(SCST_TARGETS_DIR(), $driver, SCST_MGMT_IO);
return (-w $path) ? TRUE : FALSE;
}
sub targetType {
my $self = shift;
my $driver = shift;
my $target = shift;
my $errorString;
return (undef, "Too few arguments") if (!valid($driver, $target));
if ($self->driverIsVirtualCapable($driver)) {
my $attribs;
($attribs, $errorString) = $self->targetAttributes($driver, $target);
if (defined($$attribs{'hw_target'}) &&
($$attribs{'hw_target'}->{'value'} == TRUE)) {
return $TGT_TYPE_HARDWARE;
} else {
return $TGT_TYPE_VIRTUAL;
}
}
return $TGT_TYPE_HARDWARE;
}
sub addVirtualTarget {
my $self = shift;
my $driver = shift;
my $target = shift;
my $attributes = shift;
my $o_string = "";
foreach my $attribute (keys %{$attributes}) {
if (ref($$attributes{$attribute}) eq 'ARRAY') {
foreach my $value (@{$$attributes{$attribute}}) {
$o_string .= "$attribute=$value;";
}
} else {
my $value = $$attributes{$attribute};
$o_string .= "$attribute=$value;";
}
}
$o_string =~ s/\s$//;
my ($path, $cmd);
$path = make_path(SCST_TARGETS_DIR(), $driver, SCST_MGMT_IO);
$cmd .= "add_target $target $o_string";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_EXISTS if ($rc);
return $rc if ($rc > 1);
return SCST_C_DRV_NOTVIRT if (!$self->driverIsVirtualCapable($driver));
$rc = $self->checkTargetCreateAttributes($driver, $attributes);
return SCST_C_TGT_BAD_ATTRIBUTES if ($rc == TRUE);
return $rc if ($rc > 1);
return SCST_C_TGT_ADD_FAIL;
}
sub targetDynamicAttributes {
my $self = shift;
my $driver = shift;
my %attributes = ( );
my $available;
my $errorString;
return (undef, "Too few arguments") if (!valid($driver));
my $io = new IO::File make_path(SCST_TARGETS_DIR(), $driver,
SCST_MGMT_IO), O_RDONLY;
if (!$io) {
if ($self->driverExists($driver) != TRUE) {
$errorString = "targetDynamicAttributes(): Driver '$driver' ".
"is not available";
} else {
$errorString = "targetDynamicAttributes(): Unable to open mgmt ".
"interface for driver '$driver': $!";
}
return (undef, $errorString);
}
while (my $in = <$io>) {
if ($in =~ /^The following target attributes available\:/) {
(undef, $available) = split(/\:/, $in, 2);
$available =~ s/\.$//;
}
}
close $io;
if ($available) {
foreach my $attribute (split(/\,/, $available)) {
$attribute =~ s/^\s+//;
$attribute =~ s/\s+$//;
$attributes{$attribute} = '';
}
}
return (\%attributes, undef);
}
sub checkTargetDynamicAttributes {
my $self = shift;
my $driver = shift;
my $check = shift;
my ($available, $errorString) = $self->targetDynamicAttributes($driver);
if (!defined($available)) {
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if (!$rc > 1);
return SCST_C_FATAL_ERROR;
}
return FALSE if (!defined($check));
if (ref($check) eq 'HASH') {
foreach my $attribute (keys %{$check}) {
if (!defined($$available{$attribute})) {
return TRUE;
}
}
} else {
if (!defined($$available{$check})) {
return TRUE;
}
}
return FALSE;
}
sub addTargetDynamicAttribute {
my $self = shift;
my $driver = shift;
my $target = shift;
my $attribute = shift;
my $value = shift;
return SCST_C_TGT_ADDATTR_FAIL
if (!valid($driver, $target, $attribute) || !defined($value));
my ($path, $cmd);
$path = make_path(SCST_TARGETS_DIR(), $driver, SCST_MGMT_IO);
$cmd .= "add_target_attribute $target $attribute $value";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
return SCST_C_DRV_NOTVIRT if (!$self->driverIsVirtualCapable($driver));
$rc = $self->checkTargetDynamicAttributes($driver, $attribute);
return SCST_C_TGT_BAD_ATTRIBUTES if ($rc == 1);
return $rc if ($rc > 1);
return SCST_C_TGT_ADDATTR_FAIL;
}
sub removeTargetDynamicAttribute {
my $self = shift;
my $driver = shift;
my $target = shift;
my $attribute = shift;
my $value = shift;
return SCST_C_TGT_REMATTR_FAIL
if (!valid($driver, $target, $attribute) || !defined($value));
my ($path, $cmd);
$path = make_path(SCST_TARGETS_DIR(), $driver, SCST_MGMT_IO);
$cmd .= "del_target_attribute $target $attribute $value";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
return SCST_C_DRV_NOTVIRT if (!$self->driverIsVirtualCapable($driver));
$rc = $self->checkTargetDynamicAttributes($driver, $attribute);
return SCST_C_TGT_BAD_ATTRIBUTES if ($rc == 1);
return $rc if ($rc > 1);
return SCST_C_TGT_REMATTR_FAIL;
}
sub removeVirtualTarget {
my $self = shift;
my $driver = shift;
my $target = shift;
# May fail if the target does not have an 'enabled' attribute as is the
# case for e.g. the 'scst_local' driver.
$self->enableTarget($driver, $target, FALSE);
my ($sessions, $errorString) = $self->sessions($driver, $target);
my %can_close;
foreach my $session (keys %{$sessions}) {
if (defined($$sessions{$session}->{'force_close'})) {
$can_close{$session}++;
my $rc = $self->closeSession($driver, $target, $session);
return $rc if ($rc);
}
}
if (scalar keys %can_close) {
my $has_sessions = 1;
my $now = time();
while ($has_sessions && (($now + $TIMEOUT) > time())) {
($sessions, $errorString) = $self->sessions($driver, $target);
foreach my $session (keys %can_close) {
if (!defined($$sessions{$session})) {
delete $can_close{$session};
}
}
$has_sessions = scalar keys %can_close;
sleep 1 if ($has_sessions);
}
return SCST_C_TGT_BUSY if ($has_sessions);
}
my ($path, $cmd);
$path = make_path(SCST_TARGETS_DIR(), $driver, SCST_MGMT_IO);
$cmd .= "del_target $target";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
return SCST_C_DRV_NOTVIRT if (!$self->driverIsVirtualCapable($driver));
return SCST_C_TGT_REM_FAIL;
}
sub groupExists {
my $self = shift;
my $driver = shift;
my $target = shift;
my $group = shift;
return FALSE if (!valid($driver, $target, $group));
my ($groups, $errorString) = $self->groups($driver, $target);
if (!defined($groups)) {
my $rc = $self->targetExists($driver, $target);
return FALSE if (!$rc);
return $rc if ($rc > 1);
return SCST_C_FATAL_ERROR;
}
foreach my $_group (@{$groups}) {
return TRUE if ($group eq $_group);
}
return FALSE;
}
sub initiatorExists {
my $self = shift;
my $driver = shift;
my $target = shift;
my $group = shift;
my $initiator = shift;
return FALSE if (!valid($driver, $target, $group, $initiator));
my ($initiators, $errorString) = $self->initiators($driver, $target, $group);
if (!defined($initiators)) {
my $rc = $self->groupExists($driver, $target, $group);
return FALSE if (!$rc);
return $rc if ($rc > 1);
return SCST_C_FATAL_ERROR;
}
foreach my $_initiator (@{$initiators}) {
return TRUE if ($initiator eq $_initiator);
}
return FALSE;
}
sub lunExists {
my $self = shift;
my $driver = shift;
my $target = shift;
my $lun = shift;
my $group = shift;
return FALSE if (!valid($driver, $target, $lun));
my ($luns, $errorString) = $self->luns($driver, $target, $group);
if (!defined($luns)) {
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
if (defined($group)) {
my $rc = $self->groupExists($driver, $target, $group);
return SCST_C_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
}
return SCST_C_FATAL_ERROR;
}
foreach my $_lun (keys %{$luns}) {
return TRUE if ($lun == $_lun);
}
return FALSE;
}
sub addGroup {
my $self = shift;
my $driver = shift;
my $target = shift;
my $group = shift;
return SCST_C_GRP_ADD_FAIL if (!valid($driver, $target, $group));
my ($path, $cmd);
$path = make_path(SCST_TARGETS_DIR(), $driver, $target, SCST_GROUPS,
SCST_MGMT_IO);
$cmd .= "create $group";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
$rc = $self->groupExists($driver, $target, $group);
return SCST_C_GRP_EXISTS if ($rc == TRUE);
return $rc if ($rc > 1);
return SCST_C_GRP_ADD_FAIL;
}
sub removeGroup {
my $self = shift;
my $driver = shift;
my $target = shift;
my $group = shift;
return SCST_C_GRP_REM_FAIL if (!valid($driver, $target, $group));
my ($path, $cmd);
$path = make_path(SCST_TARGETS_DIR(), $driver, $target, SCST_GROUPS,
SCST_MGMT_IO);
$cmd .= "del $group";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
$rc = $self->groupExists($driver, $target, $group);
return SCST_C_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
return SCST_C_GRP_REM_FAIL;
}
sub addDeviceGroup {
my $self = shift;
my $group = shift;
return SCST_C_DEV_GRP_ADD_FAIL if (!valid($group));
my ($path, $cmd);
$path = make_path(SCST_DEV_GROUP_DIR(), SCST_MGMT_IO);
$cmd .= "create $group";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->deviceGroupExists($group);
return $rc == TRUE ? SCST_C_DEV_GRP_EXISTS : $rc > 1 ? $rc :
SCST_C_DEV_GRP_ADD_FAIL;
}
sub removeDeviceGroup {
my $self = shift;
my $group = shift;
return SCST_C_DEV_GRP_REM_FAIL if (!valid($group));
my ($path, $cmd);
$path = make_path(SCST_DEV_GROUP_DIR(), SCST_MGMT_IO);
$cmd .= "del $group";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->deviceGroupExists($group);
return SCST_C_DEV_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
return SCST_C_DEV_GRP_REM_FAIL;
}
sub addDeviceGroupDevice {
my $self = shift;
my $group = shift;
my $device = shift;
my $dgroups;
my $errorString;
return SCST_C_DGRP_ADD_DEV_FAIL if (!valid($group, $device));
my ($path, $cmd);
$path = make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_DEVICES,
SCST_MGMT_IO);
$cmd .= "add $device";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->deviceGroupExists($group);
return SCST_C_DEV_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
$rc = $self->deviceExists($device);
return SCST_C_DEV_NO_DEVICE if (!$rc);
return $rc if ($rc > 1);
$rc = $self->deviceGroupDeviceExists($group, $device);
return SCST_C_DGRP_DEVICE_EXISTS if ($rc == TRUE);
return $rc if ($rc > 1);
# Check all device groups for this device
($dgroups, $errorString) = $self->deviceGroups();
foreach my $dgroup (@{$dgroups}) {
my $devs;
($devs, $errorString) = $self->deviceGroupDevices($dgroup);
foreach my $dev (@{$devs}) {
return SCST_C_DGRP_DEVICE_OTHER if ($dev eq $device);
}
}
return SCST_C_DGRP_ADD_DEV_FAIL;
}
sub addTargetGroup {
my $self = shift;
my $group = shift;
my $tgroup = shift;
return SCST_C_DGRP_ADD_GRP_FAIL if (!valid($group, $tgroup));
my ($path, $cmd);
$path = make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_TGROUPS,
SCST_MGMT_IO);
$cmd .= "add $tgroup";
my $bytes = - ENONENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->deviceGroupExists($group);
return SCST_C_DEV_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetGroupExists($group, $tgroup);
return SCST_C_DGRP_GROUP_EXISTS if ($rc == TRUE);
return $rc > 1 ? $rc : SCST_C_DGRP_ADD_GRP_FAIL;
}
sub addTargetGroupTarget {
my $self = shift;
my $group = shift;
my $tgroup = shift;
my $tgt = shift;
return SCST_C_TGRP_ADD_TGT_FAIL
if (!valid($group, $tgroup, $tgt));
my ($path, $cmd);
$path = make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_TGROUPS,
$tgroup, SCST_MGMT_IO);
$cmd .= "add $tgt";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->deviceGroupExists($group);
return SCST_C_DEV_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetGroupExists($group, $tgroup);
return SCST_C_DGRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetGroupTargetExists($group, $tgroup, $tgt);
return $rc == TRUE ? SCST_C_TGRP_TGT_EXISTS : $rc > 1 ? $rc : SCST_C_TGRP_ADD_TGT_FAIL;
}
sub removeDeviceGroupDevice {
my $self = shift;
my $group = shift;
my $device = shift;
return SCST_C_DGRP_REM_DEV_FAIL if (!valid($group, $device));
my ($path, $cmd);
$path = make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_DEVICES,
SCST_MGMT_IO);
$cmd .= "del $device";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->deviceGroupExists($group);
return SCST_C_DEV_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
$rc = $self->deviceExists($device);
return SCST_C_DEV_NO_DEVICE if (!$rc);
return $rc if ($rc > 1);
$rc = $self->deviceGroupDeviceExists($group, $device);
return SCST_C_DGRP_NO_DEVICE if (!$rc);
return $rc if ($rc > 1);
return SCST_C_DGRP_REM_DEV_FAIL;
}
sub removeTargetGroup {
my $self = shift;
my $group = shift;
my $tgroup = shift;
return SCST_C_DGRP_REM_GRP_FAIL if (!valid($group, $tgroup));
my ($path, $cmd);
$path = make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_TGROUPS,
SCST_MGMT_IO);
$cmd .= "del $tgroup";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->deviceGroupExists($group);
return SCST_C_DEV_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetGroupExists($group, $tgroup);
return SCST_C_DGRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
return SCST_C_DGRP_REM_GRP_FAIL;
}
sub removeTargetGroupTarget {
my $self = shift;
my $group = shift;
my $tgroup = shift;
my $tgt = shift;
return SCST_C_TGRP_REM_TGT_FAIL if (!valid($group, $tgroup, $tgt));
my ($path, $cmd);
$path = make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_TGROUPS,
$tgroup, SCST_MGMT_IO);
$cmd .= "del $tgt";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->deviceGroupExists($group);
return SCST_C_DEV_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetGroupExists($group, $tgroup);
return SCST_C_DGRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetGroupTargetExists($group, $tgroup, $tgt);
return SCST_C_TGRP_NO_TGT if (!$rc);
return $rc if ($rc > 1);
return SCST_C_TGRP_REM_TGT_FAIL;
}
sub addInitiator {
my $self = shift;
my $driver = shift;
my $target = shift;
my $group = shift;
my $initiator = shift;
return SCST_C_GRP_ADD_INI_FAIL
if (!valid($driver, $target, $group, $initiator));
my ($path, $cmd);
$path = make_path(SCST_TARGETS_DIR(), $driver, $target, SCST_GROUPS,
$group, SCST_INITIATORS, SCST_MGMT_IO);
$cmd .= "add $initiator";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
return SCST_C_GRP_REM_INI_FAIL
if (!valid($driver, $target, $initiator));
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
$rc = $self->groupExists($driver, $target, $group);
return SCST_C_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
$rc = $self->initiatorExists($driver, $target, $group, $initiator);
return SCST_C_GRP_INI_EXISTS if ($rc == TRUE);
return $rc if ($rc > 1);
return SCST_C_GRP_ADD_INI_FAIL;
}
sub removeInitiator {
my $self = shift;
my $driver = shift;
my $target = shift;
my $group = shift;
my $initiator = shift;
return SCST_C_GRP_REM_INI_FAIL
if (!valid($driver, $target, $group, $initiator));
my ($path, $cmd);
$path = make_path(SCST_TARGETS_DIR(), $driver, $target, SCST_GROUPS,
$group, SCST_INITIATORS, SCST_MGMT_IO);
$cmd .= "del $initiator";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
$rc = $self->groupExists($driver, $target, $group);
return SCST_C_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
$rc = $self->initiatorExists($driver, $target, $group, $initiator);
return SCST_C_GRP_NO_INI if (!$rc);
return $rc if ($rc > 1);
return SCST_C_GRP_REM_INI_FAIL;
}
sub moveInitiator {
my $self = shift;
my $driver = shift;
my $target = shift;
my $from = shift;
my $to = shift;
my $initiator = shift;
my ($path, $cmd);
$path = make_path(SCST_TARGETS_DIR(), $driver, $target, SCST_GROUPS,
$from, SCST_INITIATORS, SCST_MGMT_IO);
$cmd .= "move $initiator $to";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
$rc = $self->groupExists($driver, $target, $from);
return SCST_C_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
$rc = $self->groupExists($driver, $target, $to);
return SCST_C_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
$rc = $self->initiatorExists($driver, $target, $from, $initiator);
return SCST_C_GRP_NO_INI if (!$rc);
return $rc if ($rc > 1);
$rc = $self->initiatorExists($driver, $target, $to, $initiator);
return SCST_C_GRP_INI_EXISTS if ($rc == TRUE);
return $rc if ($rc > 1);
return SCST_C_GRP_MOV_INI_FAIL;
}
sub clearInitiators {
my $self = shift;
my $driver = shift;
my $target = shift;
my $group = shift;
my ($path, $cmd);
$path = make_path(SCST_TARGETS_DIR(), $driver, $target, SCST_GROUPS,
$group, SCST_INITIATORS, SCST_MGMT_IO);
$cmd .= "clear";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
$rc = $self->groupExists($driver, $target, $group);
return SCST_C_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
return SCST_C_GRP_CLR_INI_FAIL;
}
sub addLun {
my $self = shift;
my $driver = shift;
my $target = shift;
my $device = shift;
my $lun = shift;
my $attributes = shift;
my $group = shift;
my $err = valid($group) ? SCST_C_GRP_ADD_LUN_FAIL :
SCST_C_TGT_ADD_LUN_FAIL;
return $err if (!valid($driver, $target, $device, $lun, $attributes));
my $o_string = "";
foreach my $attribute (keys %{$attributes}) {
my $value = $$attributes{$attribute};
$o_string .= "$attribute=$value;";
}
$o_string =~ s/\s$//;
my ($path, $cmd);
if (valid($group)) {
$path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_GROUPS, $group, SCST_LUNS, SCST_MGMT_IO);
} else {
$path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_LUNS, SCST_MGMT_IO);
}
$cmd .= "add $device $lun $o_string";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
if (valid($group)) {
my $rc = $self->groupExists($driver, $target, $group);
return SCST_C_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
}
my $rc = $self->lunExists($driver, $target, $lun, $group);
return (valid($group) ? SCST_C_GRP_LUN_EXISTS :
SCST_C_TGT_LUN_EXISTS) if ($rc == TRUE);
return $rc if ($rc > 1);
$rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
$rc = $self->deviceOpen($device);
return SCST_C_DEV_NO_DEVICE if (!$rc);
return $rc if ($rc > 1);
$rc = $self->checkLunCreateAttributes($driver, $target, $attributes, $group);
return SCST_C_LUN_BAD_ATTRIBUTES if ($rc == TRUE);
return $rc if ($rc > 1);
return $err;
}
sub removeLun {
my $self = shift;
my $driver = shift;
my $target = shift;
my $lun = shift;
my $group = shift;
my $err = valid($group) ? SCST_C_GRP_REM_LUN_FAIL :
SCST_C_TGT_ADD_LUN_FAIL;
return $err if (!valid($driver, $target, $lun));
my ($path, $cmd);
if (valid($group)) {
$path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_GROUPS, $group, SCST_LUNS,
SCST_MGMT_IO);
} else {
$path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_LUNS, SCST_MGMT_IO);
}
$cmd .= "del $lun";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
if (valid($group)) {
$rc = $self->groupExists($driver, $target, $group);
return SCST_C_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
}
$rc = $self->lunExists($driver, $target, $lun, $group);
return (valid($group) ? SCST_C_GRP_NO_LUN :
SCST_C_TGT_NO_LUN) if (!$rc);
return $rc if ($rc > 1);
return $err;
}
sub replaceLun {
my $self = shift;
my $driver = shift;
my $target = shift;
my $lun = shift;
my $device = shift;
my $attributes = shift;
my $group = shift;
my $err;
return TRUE if (!valid($lun));
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
if (valid($group)) {
$rc = $self->groupExists($driver, $target, $group);
return SCST_C_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
$err = SCST_C_GRP_NO_LUN;
} else {
$err = SCST_C_TGT_NO_LUN;
}
$rc = $self->lunExists($driver, $target, $lun, $group);
return $err if (!$rc);
return $rc if ($rc > 1);
$rc = $self->deviceOpen($device);
return SCST_C_DEV_NO_DEVICE if (!$rc);
return $rc if ($rc > 1);
$rc = $self->checkLunCreateAttributes($driver, $target, $attributes, $group);
return SCST_C_LUN_BAD_ATTRIBUTES if ($rc == TRUE);
return $rc if ($rc > 1);
my ($luns, $errorString) = $self->luns($driver, $target, $group);
return SCST_C_LUN_DEV_EXISTS if ($$luns{$lun} eq $device);
my $o_string = "";
foreach my $attribute (keys %{$attributes}) {
my $value = $$attributes{$attribute};
$o_string .= "$attribute=$value;";
}
$o_string =~ s/\s$//;
my ($path, $cmd);
if (defined($group)) {
$path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_GROUPS, $group, SCST_LUNS,
SCST_MGMT_IO);
} else {
$path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_LUNS, SCST_MGMT_IO);
}
$cmd .= "replace $device $lun $o_string";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
return SCST_C_LUN_RPL_DEV_FAIL;
}
sub clearLuns {
my $self = shift;
my $driver = shift;
my $target = shift;
my $group = shift;
my $err = valid($group) ? SCST_C_GRP_CLR_LUN_FAIL :
SCST_C_TGT_CLR_LUN_FAIL;
return $err if (!valid($driver, $target));
my ($path, $cmd);
if (valid($group)) {
$path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_GROUPS, $group, SCST_LUNS,
SCST_MGMT_IO);
} else {
$path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_LUNS, SCST_MGMT_IO);
}
$cmd .= "clear";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
if (valid($group)) {
$rc = $self->groupExists($driver, $target, $group);
return SCST_C_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
}
return $err;
}
sub deviceHandler {
my $self = shift;
my $device = shift;
return undef if (!valid($device));
my $handler = readlink(make_path(SCST_DEVICES_DIR(), $device,
'handler'));
$handler =~ s/.*\///;
return $handler;
}
sub devices {
my $self = shift;
my $handler = shift;
my @devices;
my $dHandle = new IO::Handle;
my $_path = !valid($handler) ? SCST_DEVICES_DIR() :
make_path(SCST_HANDLERS_DIR(), $handler);
if (!(opendir $dHandle, $_path)) {
return (undef, "devices(): Unable to read directory '$_path': $!");
}
foreach my $device (readdir($dHandle)) {
next if ($device eq '.' || $device eq '..');
my $isdev = (-d make_path(SCST_DEVICES_DIR(), $device));
if ($isdev && (!valid($handler) ||
$handler eq $self->deviceHandler($device))) {
push @devices, $device;
}
}
close $dHandle;
return (\@devices, undef);
}
sub deviceOpen {
my $self = shift;
my $device = shift;
return FALSE if (!valid($device));
my ($devices, $errorString) = $self->devices();
return SCST_C_FATAL_ERROR if (!valid($devices));
foreach my $_device (@{$devices}) {
return TRUE if ($device eq $_device);
}
return FALSE;
}
sub deviceAttributes {
my $self = shift;
my $device = shift;
my %attributes = ( );
my $errorString;
my $dca;
my $pHandle = new IO::Handle;
my $_path = make_path(SCST_DEVICES_DIR(), $device);
if (!(opendir $pHandle, $_path)) {
if ($self->deviceOpen($device) != TRUE) {
$errorString = "deviceAttributes(): Device '$device' is not open";
} else {
$errorString = "deviceAttributes(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
($dca, $errorString) = $self->deviceCreateAttributes($self->deviceHandler($device));
foreach my $attribute (readdir($pHandle)) {
next if ($attribute eq '.' || $attribute eq '..' ||
$attribute eq SCST_MGMT_IO || $attribute eq 'uevent');
my $pPath = make_path(SCST_DEVICES_DIR(), $device, $attribute);
my $mode = (stat($pPath))[2];
if ($attribute eq 'exported') {
my $eHandle = new IO::Handle;
opendir $eHandle, make_path(SCST_DEVICES_DIR(),
$device, $attribute);
foreach my $export (readdir($eHandle)) {
next if (($export eq '.') || ($export eq '..'));
my $linked = readlink make_path($pPath, $export);
my $t = SCST_TARGETS;
my $g = SCST_GROUPS;
my $l = SCST_LUNS;
my ($driver, $target, $group, $lun) = "" x 4;
if ($linked =~ /^(\.\.\/)*$t\/([^\/]+)\/([^\/]+)\/$g\/([^\/]+)\/$l\/(\d+)$/) {
$driver = $2;
$target = $3;
$group = $4;
$lun = $5;
} elsif ($linked =~ /^(\.\.\/)*$t\/([^\/]+)\/([^\/]+)\/$l\/(\d+)$/) {
$driver = $2;
$target = $3;
$group = "";
$lun = $4;
} else {
print("internal error: could not parse $linked\n");
next;
}
next if ($driver eq 'copy_manager');
$attributes{$attribute}->{'value'}->{$driver}->{$target}->{$group} = $lun;
}
if ($attributes{$attribute}->{'value'}) {
$attributes{$attribute}->{'static'} = TRUE;
}
close $eHandle;
} elsif ($attribute eq 'handler') {
my $linked = readlink $pPath;
$linked =~ s/.*\///;
$attributes{$attribute}->{'static'} = TRUE;
$attributes{$attribute}->{'value'} = $linked;
} elsif ($attribute eq 'scsi_device') {
my $linked = readlink $pPath;
$linked =~ s/.*\///;
$attributes{$attribute}->{'static'} = TRUE;
$attributes{$attribute}->{'value'} = $linked;
} else {
if (!(($mode & S_IRUSR) >> 6)) {
$attributes{$attribute}->{'static'} = FALSE;
$attributes{$attribute}->{'value'} = undef;
} else {
my $is_static;
if ($attribute eq 'cpu_mask' ||
$attribute eq 'filename' ||
$attribute eq 'threads_num' ||
$attribute eq 'threads_pool_type' ||
($mode & S_IWUSR) >> 6) {
$is_static = FALSE;
} else {
$is_static = TRUE;
}
my $io = new IO::File $pPath, O_RDONLY;
if (!$io) {
return (undef, "deviceAttributes(): Unable to read ".
"device attribute '$attribute': $!");
}
my ($value, $is_key) = split("\n", _sysread($io) , 2);
close $io;
$value = "" if (!defined($value));
chomp $value;
$is_key = $is_key && $is_key =~ /\[key\]/;
my $key = 0;
if ($is_key) {
if ($attribute =~ /^([^\d]+)(\d+)$/) {
$attribute = $1;
$key = $2;
}
}
next if ($attribute eq SCST_MGMT_IO);
if ($attribute eq 'type') {
my($type, $type_string) = split(/\s\-\s/, $value, 2);
$attributes{$attribute}->{'value'} = $type;
$attributes{'type_string'}->{'value'} = $type_string;
$attributes{'type_string'}->{'static'} = TRUE;
} else {
if ($is_key) {
$attributes{$attribute}->{'keys'}->{$key}->{'value'} = $value;
} else {
$attributes{$attribute}->{'value'} = $value;
}
}
$attributes{$attribute}->{'static'} = $is_static;
}
}
}
close $pHandle;
return (\%attributes, undef);
}
sub driverAttributes {
my $self = shift;
my $driver = shift;
my %attributes = ( );
my $errorString;
my $pHandle = new IO::Handle;
my $_path = make_path(SCST_TARGETS_DIR(), $driver);
if (!(opendir $pHandle, $_path)) {
if ($self->driverExists($driver) != TRUE) {
$errorString = "driverAttributes(): Driver '$driver' is not available";
} else {
$errorString = "driverAttributes(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
foreach my $attribute (readdir($pHandle)) {
next if ($attribute eq '.' || $attribute eq '..' ||
$attribute eq SCST_MGMT_IO);
my $pPath = make_path(SCST_TARGETS_DIR(), $driver, $attribute);
my $mode = (stat($pPath))[2];
if (-d $pPath) {
# Skip directories
} else {
if (!(($mode & S_IRUSR) >> 6)) {
$attributes{$attribute}->{'static'} = FALSE;
$attributes{$attribute}->{'value'} = undef;
} else {
my $is_static;
if (($mode & S_IWUSR) >> 6) {
$is_static = FALSE;
} else {
$is_static = TRUE;
}
my $io = new IO::File $pPath, O_RDONLY;
if (!$io) {
return (undef, "driverAttributes(): Unable to read ".
"driver attribute '$attribute': $!");
}
my $value = <$io>;
$value = "" if (!defined($value));
chomp $value;
my $is_key = <$io>;
$is_key = $is_key && $is_key =~ /\[key\]/;
my $key = 0;
if ($is_key) {
if ($attribute =~ /^([^\d]+)(\d+)$/) {
$attribute = $1;
$key = $2;
}
}
next if ($attribute eq SCST_MGMT_IO);
if ($attribute eq SCST_TRACE_IO) {
$attributes{$attribute}->{'value'} = $value;
my @possible;
$value = '';
my $start = FALSE;
while (my $line = <$io>) {
$start = TRUE if ($line !~ /\[/);
$value .= $line if ($start);
}
$value =~ s/\n//g;
if ($value =~ /\[(.*)\]/) {
$value = $1;
foreach my $t (split(/\,/, $value)) {
$t =~ s/^\s+//; $t =~ s/\s+$//;
push @possible, $t;
}
}
$attributes{$attribute}->{'set'} = \@possible;
} else {
if ($is_key) {
$attributes{$attribute}->{'keys'}->{$key}->{'value'} = $value;
} else {
$attributes{$attribute}->{'value'} = $value;
}
}
close $io;
$attributes{$attribute}->{'static'} = $is_static;
}
}
}
close $pHandle;
return (\%attributes, undef);
}
sub setDriverAttribute {
my $self = shift;
my $driver = shift;
my $attribute = shift;
my $value = shift;
return TRUE if (!valid($attribute) || !defined($value));
my $path = make_path(SCST_TARGETS_DIR(), $driver, $attribute);
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $attribute = $value\n";
} else {
$value .= "\n";
$bytes = _syswrite($io, $value, length($value));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
return setAttrFailed($path, $bytes, SCST_C_DRV_BAD_ATTRIBUTES,
SCST_C_DRV_ATTRIBUTE_STATIC,
SCST_C_DRV_SETATTR_FAIL);
}
sub targetAttributes {
my $self = shift;
my $driver = shift;
my $target = shift;
my %attributes = ( );
my $errorString;
return (undef, "Too few arguments") if (!valid($driver, $target));
my $pHandle = new IO::Handle;
my $_path = make_path(SCST_TARGETS_DIR(), $driver, $target);
if (!(opendir $pHandle, $_path)) {
if ($self->driverExists($driver) != TRUE) {
$errorString = "targetAttributes(): Driver '$driver' is not available";
} elsif ($self->targetExists($driver, $target) != TRUE) {
$errorString = "targetAttributes(): Target '$target' is not available";
} else {
$errorString = "targetAttributes(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
foreach my $attribute (readdir($pHandle)) {
next if ($attribute eq '.' || $attribute eq '..' ||
$attribute eq SCST_MGMT_IO || $attribute eq 'uevent');
my $pPath = make_path(SCST_TARGETS_DIR(), $driver, $target,
$attribute);
my $mode = (stat($pPath))[2];
if ($attribute eq 'host') {
my $linked = readlink($pPath);
$linked =~ s/.*\///;
$attributes{$attribute}->{'static'} = TRUE;
$attributes{$attribute}->{'value'} = $linked;
} elsif ($driver eq 'scst_local' && $attribute eq 'sessions') {
my $_session_path = make_path($_path, $attribute);
my $pSessHandle = new IO::Handle;
if (!(opendir $pSessHandle, $_session_path)) {
return (undef, "targetAttributes(): Unable to read directory '$_session_path': $!");
}
my $key = 0;
foreach my $e (readdir($pSessHandle)) {
next if ($e eq '.' || $e eq '..');
$attributes{'session_name'}->{'keys'}->{$key}->{'value'} = $e;
$key++;
}
} elsif (-d $pPath) {
# Skip directories
} else {
if (!(($mode & S_IRUSR) >> 6)) {
$attributes{$attribute}->{'static'} = FALSE;
$attributes{$attribute}->{'value'} = undef;
} else {
my $is_static;
if ($attribute eq 'enabled' ||
$attribute eq 'cpu_mask' ||
($mode & S_IWUSR) >> 6) {
$is_static = FALSE;
} else {
$is_static = TRUE;
}
my $io = new IO::File $pPath, O_RDONLY;
if (!$io) {
return (undef, "targetAttributes(): Unable to read ".
"target attribute '$attribute': $!");
}
my $value = <$io>;
$value = "" if (!defined($value));
chomp $value;
my $is_key = <$io>;
close $io;
$is_key = $is_key && $is_key =~ /\[key\]/;
my $key = 0;
if ($is_key) {
if ($attribute =~ /^([^\d]+)(\d+)$/) {
$attribute = $1;
$key = $2;
}
}
next if ($attribute eq SCST_MGMT_IO);
$attributes{$attribute}->{'static'} = $is_static;
if ($is_key) {
$attributes{$attribute}->{'keys'}->{$key}->{'value'} = $value;
} else {
$attributes{$attribute}->{'value'} = $value;
}
}
}
}
close $pHandle;
return (\%attributes, undef);
}
sub setTargetAttribute {
my $self = shift;
my $driver = shift;
my $target = shift;
my $attribute = shift;
my $value = shift;
return TRUE if (!valid($driver, $target, $attribute) ||
!defined($value));
my ($path, $cmd);
$path = make_path(SCST_TARGETS_DIR(), $driver, $target, $attribute);
$cmd = $value;
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $cmd > $path\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
return setAttrFailed($path, $bytes, SCST_C_TGT_BAD_ATTRIBUTES,
SCST_C_TGT_ATTRIBUTE_STATIC,
SCST_C_TGT_SETATTR_FAIL);
}
sub groupAttributes {
my $self = shift;
my $driver = shift;
my $target = shift;
my $group = shift;
my %attributes = ( );
my $errorString;
my $pHandle = new IO::Handle;
my $_path = make_path(SCST_TARGETS_DIR(), $driver, $target, SCST_GROUPS,
$group);
if (!(opendir $pHandle, $_path)) {
if ($self->driverExists($driver) != TRUE) {
$errorString = "groupAttributes(): Driver '$driver' is not available";
} elsif ($self->targetExists($driver, $target) != TRUE) {
$errorString = "groupAttributes(): Target '$target' is not available";
} elsif ($self->groupExists($driver, $target, $group) != TRUE) {
$errorString = "groupAttributes(): Group '$group' does not exist";
} else {
$errorString = "groupAttributes(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
foreach my $attribute (readdir($pHandle)) {
next if ($attribute eq '.' || $attribute eq '..' ||
$attribute eq SCST_MGMT_IO);
my $pPath = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_GROUPS, $group, $attribute);
my $mode = (stat($pPath))[2];
if (-d $pPath) {
# Skip directories
} else {
if (!(($mode & S_IRUSR) >> 6)) {
$attributes{$attribute}->{'static'} = FALSE;
$attributes{$attribute}->{'value'} = undef;
} else {
my $is_static;
if ($attribute eq 'cpu_mask' ||
($mode & S_IWUSR) >> 6) {
$is_static = FALSE;
} else {
$is_static = TRUE;
}
my $io = new IO::File $pPath, O_RDONLY;
if (!$io) {
return (undef, "groupAttributes(): Unable to read ".
"group attribute '$attribute': $!");
}
my $value = <$io>;
$value = "" if (!defined($value));
chomp $value;
my $is_key = <$io>;
close $io;
$is_key = $is_key && $is_key =~ /\[key\]/;
my $key = 0;
if ($is_key) {
if ($attribute =~ /^([^\d]+)(\d+)$/) {
$attribute = $1;
$key = $2;
}
}
next if ($attribute eq SCST_MGMT_IO);
$attributes{$attribute}->{'static'} = $is_static;
if ($is_key) {
$attributes{$attribute}->{'keys'}->{$key}->{'value'} = $value;
} else {
$attributes{$attribute}->{'value'} = $value;
}
}
}
}
close $pHandle;
return (\%attributes, undef);
}
sub setGroupAttribute {
my $self = shift;
my $driver = shift;
my $target = shift;
my $group = shift;
my $attribute = shift;
my $value = shift;
return TRUE if (!valid($attribute) || !defined($value));
my ($path, $cmd);
$path = make_path(SCST_TARGETS_DIR(), $driver, $target, SCST_GROUPS,
$group, $attribute);
$cmd .= $value;
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $cmd > $path\n";
} else {
$value .= "\n";
$bytes = _syswrite($io, $value, length($value));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
$rc = $self->groupExists($driver, $target, $group);
return SCST_C_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
return setAttrFailed($path, $bytes, SCST_C_GRP_BAD_ATTRIBUTES,
SCST_C_GRP_ATTRIBUTE_STATIC,
SCST_C_GRP_SETATTR_FAIL);
}
sub lunAttributes {
my $self = shift;
my $driver = shift;
my $target = shift;
my $lun = shift;
my $group = shift;
my $errorString;
my %attributes = ( );
my ($_path, $luncrattr);
if (valid($group)) {
$_path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_GROUPS, $group, SCST_LUNS, $lun);
($luncrattr, $errorString) =
$self->lunCreateAttributes($driver, $target, $group);
} else {
$_path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_LUNS, $lun);
($luncrattr, $errorString) = $self->lunCreateAttributes($driver, $target);
}
my $pHandle = new IO::Handle;
if (!(opendir $pHandle, $_path)) {
if ($self->driverExists($driver) != TRUE) {
$errorString = "lunAttributes(): Driver '$driver' is not available";
} elsif ($self->targetExists($driver, $target) != TRUE) {
$errorString = "lunAttributes(): Target '$target' is not available";
} elsif (valid($group) &&
$self->groupExists($driver, $target, $group) != TRUE) {
$errorString = "lunAttributes(): Group '$group' does not exist";
} elsif ($self->lunExists($driver, $target, $lun, $group) != TRUE) {
$errorString = "lunAttributes(): LUN '$lun' does not exist";
} else {
$errorString = "lunAttributes(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
foreach my $attribute (readdir($pHandle)) {
next if ($attribute eq '.' || $attribute eq '..' ||
$attribute eq SCST_MGMT_IO);
my $pPath = make_path($_path, $attribute);
my $mode = (stat($pPath))[2];
if ($attribute eq 'device') {
my $linked = readlink($pPath);
$linked =~ s/.*\///;
$attributes{$attribute}->{'static'} = TRUE;
$attributes{$attribute}->{'value'} = $linked;
} elsif (-d $pPath) {
# Skip directories
} else {
if (!(($mode & S_IRUSR) >> 6)) {
$attributes{$attribute}->{'static'} = FALSE;
$attributes{$attribute}->{'value'} = undef;
} else {
my $is_static;
if (($mode & S_IWUSR) >> 6) {
$is_static = FALSE;
} else {
$is_static = TRUE;
}
my $io = new IO::File $pPath, O_RDONLY;
if (!$io) {
return (undef, "lunAttributes(): Unable to read ".
"lun attribute '$attribute': $!");
}
my $value = <$io>;
$value = "" if (!defined($value));
chomp $value;
my $is_key = <$io>;
close $io;
$is_key = $is_key && $is_key =~ /\[key\]/;
my $key = 0;
if ($is_key) {
if ($attribute =~ /^([^\d]+)(\d+)$/) {
$attribute = $1;
$key = $2;
}
}
next if ($attribute eq SCST_MGMT_IO);
$attributes{$attribute}->{'static'} = $is_static;
if ($is_key) {
$attributes{$attribute}->{'keys'}->{$key}->{'value'} = $value;
} else {
$attributes{$attribute}->{'value'} = $value;
}
}
}
}
close $pHandle;
return (\%attributes, undef);
}
sub setLunAttribute {
my $self = shift;
my $driver = shift;
my $target = shift;
my $lun = shift;
my $attribute = shift;
my $value = shift;
my $group = shift;
return SCST_C_LUN_SETATTR_FAIL
if (!valid($driver, $target, $lun, $attribute) || !defined($value));
my $path;
if (valid($group)) {
$path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_GROUPS, $group, SCST_LUNS, $lun,
$attribute);
} else {
$path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_LUNS, $lun, $attribute);
}
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $attribute = $value\n";
} else {
$value .= "\n";
$bytes = _syswrite($io, $value, length($value));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
if (valid($group)) {
$rc = $self->groupExists($driver, $target, $group);
return SCST_C_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
}
$rc = $self->lunExists($driver, $target, $lun, $group);
return (valid($group) ? SCST_C_GRP_NO_LUN :
SCST_C_TGT_NO_LUN) if (!$rc);
return $rc if ($rc > 1);
my ($attributes, $errorString) = $self->lunAttributes($driver, $target, $lun, $group);
return SCST_C_LUN_BAD_ATTRIBUTES if (!defined($$attributes{$attribute}));
return SCST_C_LUN_ATTRIBUTE_STATIC if ($$attributes{$attribute}->{'static'});
return SCST_C_LUN_SETATTR_FAIL;
}
sub initiatorAttributes {
my $self = shift;
my $driver = shift;
my $target = shift;
my $group = shift;
my $initiator = shift;
my %attributes = ( );
my $errorString;
my $pHandle = new IO::Handle;
my $_path = make_path(SCST_TARGETS_DIR(), $driver, $target, SCST_GROUPS,
$group, SCST_INITIATORS, $initiator);
if (!(opendir $pHandle, $_path)) {
if ($self->driverExists($driver) != TRUE) {
$errorString = "initiatorAttributes(): Driver '$driver' is not available";
} elsif ($self->targetExists($driver, $target) != TRUE) {
$errorString = "initiatorAttributes(): Target '$target' is not available";
} elsif ($self->groupExists($driver, $target, $group) != TRUE) {
$errorString = "initiatorAttributes(): Group '$group' does not exist";
} elsif ($self->initiatorExists($driver, $target, $group, $initiator) != TRUE) {
$errorString = "initiatorAttributes(): Initiator '$initiator' does not exist";
} else {
$errorString = "initiatorAttributes(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
foreach my $attribute (readdir($pHandle)) {
next if ($attribute eq '.' || $attribute eq '..' ||
$attribute eq SCST_MGMT_IO);
my $pPath = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_GROUPS, $group, SCST_INITIATORS,
$initiator, $attribute);
my $mode = (stat($pPath))[2];
if (-d $pPath) {
# Skip directories
} else {
if (!(($mode & S_IRUSR) >> 6)) {
$attributes{$attribute}->{'static'} = FALSE;
$attributes{$attribute}->{'value'} = undef;
} else {
my $is_static;
if (($mode & S_IWUSR) >> 6) {
$is_static = FALSE;
} else {
$is_static = TRUE;
}
my $io = new IO::File $pPath, O_RDONLY;
if (!$io) {
return (undef, "initiatorAttributes(): Unable to read ".
"initiator attribute '$attribute': $!");
}
my $value = <$io>;
$value = "" if (!defined($value));
chomp $value;
my $is_key = <$io>;
close $io;
$is_key = $is_key && $is_key =~ /\[key\]/;
my $key = 0;
if ($is_key) {
if ($attribute =~ /^([^\d]+)(\d+)$/) {
$attribute = $1;
$key = $2;
}
}
next if ($attribute eq SCST_MGMT_IO);
$attributes{$attribute}->{'static'} = $is_static;
if ($is_key) {
$attributes{$attribute}->{'keys'}->{$key}->{'value'} = $value;
} else {
$attributes{$attribute}->{'value'} = $value;
}
}
}
}
close $pHandle;
return (\%attributes, undef);
}
sub setInitiatorAttribute {
my $self = shift;
my $driver = shift;
my $target = shift;
my $group = shift;
my $initiator = shift;
my $attribute = shift;
my $value = shift;
return TRUE if (!valid($attribute) || !defined($value));
my $path = make_path(SCST_TARGETS_DIR(), $driver, $target, SCST_GROUPS,
$group, SCST_LUNS, $initiator, $attribute);
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $attribute = $value\n";
} else {
$value .= "\n";
$bytes = _syswrite($io, $value, length($value));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
$rc = $self->groupExists($driver, $target, $group);
return SCST_C_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
$rc = $self->initiatorExists($driver, $target, $group, $initiator);
return SCST_C_GRP_NO_INI if (!$rc);
return $rc if ($rc > 1);
return SCST_C_INI_SETATTR_FAIL;
}
sub deviceGroupAttributes {
my $self = shift;
my $group = shift;
my %attributes = ( );
my $errorString;
my $pHandle = new IO::Handle;
my $_path = make_path(SCST_DEV_GROUP_DIR(), $group);
if (!(opendir $pHandle, $_path)) {
if ($self->deviceGroupExists($group) != TRUE) {
$errorString = "deviceGroupAttributes(): Device group '$group' does not exist";
} else {
$errorString = "deviceGroupAttributes(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
foreach my $attribute (readdir($pHandle)) {
next if ($attribute eq '.' || $attribute eq '..' ||
$attribute eq SCST_MGMT_IO);
my $pPath = make_path(SCST_DEV_GROUP_DIR(), $group, $attribute);
my $mode = (stat($pPath))[2];
if (-d $pPath) {
# Skip directories
} else {
if (!(($mode & S_IRUSR) >> 6)) {
$attributes{$attribute}->{'static'} = FALSE;
$attributes{$attribute}->{'value'} = undef;
} else {
my $is_static;
if (($mode & S_IWUSR) >> 6) {
$is_static = FALSE;
} else {
$is_static = TRUE;
}
my $io = new IO::File $pPath, O_RDONLY;
if (!$io) {
return (undef, "deviceGroupAttributes(): Unable to read ".
"device group attribute '$attribute': $!");
}
my $value = <$io>;
$value = "" if (!defined($value));
chomp $value;
my $is_key = <$io>;
close $io;
$is_key = $is_key && $is_key =~ /\[key\]/;
my $key = 0;
if ($is_key) {
if ($attribute =~ /^([^\d]+)(\d+)$/) {
$attribute = $1;
$key = $2;
}
}
next if ($attribute eq SCST_MGMT_IO);
$attributes{$attribute}->{'static'} = $is_static;
if ($is_key) {
$attributes{$attribute}->{'keys'}->{$key}->{'value'} = $value;
} else {
$attributes{$attribute}->{'value'} = $value;
}
}
}
}
close $pHandle;
return (\%attributes, undef);
}
sub targetGroupAttributes {
my $self = shift;
my $group = shift;
my $tgroup = shift;
my %attributes = ( );
my $errorString;
my $pHandle = new IO::Handle;
my $_path = make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_TGROUPS, $tgroup);
if (!(opendir $pHandle, $_path)) {
if ($self->deviceGroupExists($group) != TRUE) {
$errorString = "targetGroupAttributes(): Device group '$group' does not exist";
} elsif ($self->targetGroupExists($group, $tgroup) != TRUE) {
$errorString = "targetGroupAttributes(): Target Group '$tgroup' does not exist";
} else {
$errorString = "targetGroupAttributes(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
foreach my $attribute (readdir($pHandle)) {
next if ($attribute eq '.' || $attribute eq '..' ||
$attribute eq SCST_MGMT_IO);
my $pPath = make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_TGROUPS, $tgroup, $attribute);
my $mode = (stat($pPath))[2];
if (-d $pPath) {
# Skip directories
} else {
if (!(($mode & S_IRUSR) >> 6)) {
$attributes{$attribute}->{'static'} = FALSE;
$attributes{$attribute}->{'value'} = undef;
} else {
my $is_static;
if (($mode & S_IWUSR) >> 6) {
$is_static = FALSE;
} else {
$is_static = TRUE;
}
my $io = new IO::File $pPath, O_RDONLY;
if (!$io) {
return (undef, "targetGroupAttributes(): Unable to read ".
"target group attribute '$attribute': $!");
}
my $value = <$io>;
$value = "" if (!defined($value));
chomp $value;
my $is_key = <$io>;
close $io;
$is_key = $is_key && $is_key =~ /\[key\]/;
my $key = 0;
if ($is_key) {
if ($attribute =~ /^([^\d]+)(\d+)$/) {
$attribute = $1;
$key = $2;
}
}
next if ($attribute eq SCST_MGMT_IO);
$attributes{$attribute}->{'static'} = $is_static;
if ($is_key) {
$attributes{$attribute}->{'keys'}->{$key}->{'value'} = $value;
} else {
$attributes{$attribute}->{'value'} = $value;
}
}
}
}
close $pHandle;
return (\%attributes, undef);
}
sub targetGroupTargetAttributes {
my $self = shift;
my $group = shift;
my $tgroup = shift;
my $tgt = shift;
my $local_tgt = shift;
my %attributes = ( );
my $errorString;
my $pHandle = new IO::Handle;
my $_path = make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_TGROUPS, $tgroup, $tgt);
if (-l $_path && !$local_tgt) {
return (\%attributes, undef);
}
if (!(opendir $pHandle, $_path)) {
if ($self->deviceGroupExists($group) != TRUE) {
$errorString = "targetGroupTargetAttributes(): Device group '$group' does not exist";
} elsif ($self->targetGroupExists($group, $tgroup) != TRUE) {
$errorString = "targetGroupTargetAttributes(): Target Group '$tgroup' does not exist";
} elsif ($self->targetGroupTargetExists($group, $tgroup, $tgt) != TRUE) {
$errorString = "targetGroupTargetAttributes(): Target '$tgt' does not exist";
} else {
$errorString = "targetGroupTargetAttributes(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
foreach my $attribute (readdir($pHandle)) {
next if ($attribute eq '.' || $attribute eq '..' ||
$attribute eq SCST_MGMT_IO);
my $pPath = make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_TGROUPS,
$tgroup, $tgt, $attribute);
my $mode = (stat($pPath))[2];
if (-d $pPath) {
# Skip directories
} else {
if (!(($mode & S_IRUSR) >> 6)) {
$attributes{$attribute}->{'static'} = FALSE;
$attributes{$attribute}->{'value'} = undef;
} else {
my $is_static;
if (($mode & S_IWUSR) >> 6) {
$is_static = FALSE;
} else {
$is_static = TRUE;
}
my $io = new IO::File $pPath, O_RDONLY;
if (!$io) {
return (undef, "targetGroupTargetAttributes(): Unable to read ".
"target group target attribute '$attribute': $!");
}
my $value = <$io>;
$value = "" if (!defined($value));
chomp $value;
my $is_key = <$io>;
close $io;
$is_key = $is_key && $is_key =~ /\[key\]/;
my $key = 0;
if ($is_key) {
if ($attribute =~ /^([^\d]+)(\d+)$/) {
$attribute = $1;
$key = $2;
}
}
next if ($attribute eq SCST_MGMT_IO);
$attributes{$attribute}->{'static'} = $is_static;
if ($is_key) {
$attributes{$attribute}->{'keys'}->{$key}->{'value'} = $value;
} else {
$attributes{$attribute}->{'value'} = $value;
}
}
}
}
close $pHandle;
return (\%attributes, undef);
}
sub setAluaAttribute {
my $self = shift;
my $attribute = shift;
my $value = shift;
return TRUE if (!valid($attribute) || !defined($value));
my $bytes = - ENOENT;
my $path = make_path(SCST_DEV_GROUP_DIR(), $attribute);
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $value > $path\n";
} else {
$value .= "\n";
$bytes = _syswrite($io, $value, length($value));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
return setAttrFailed($path, $bytes, SCST_C_ALUA_BAD_ATTRIBUTES,
SCST_C_ALUA_ATTRIBUTE_STATIC,
SCST_C_ALUA_SETATTR_FAIL);
}
sub setDeviceGroupAttribute {
my $self = shift;
my $group = shift;
my $attribute = shift;
my $value = shift;
return TRUE if (!valid($attribute) || !defined($value));
my $bytes = - ENOENT;
my $path = make_path(SCST_DEV_GROUP_DIR(), $group, $attribute);
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $attribute = $value\n";
} else {
$value .= "\n";
$bytes = _syswrite($io, $value, length($value));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->deviceGroupExists($group);
return SCST_C_DEV_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
return setAttrFailed($path, $bytes, SCST_C_DGRP_BAD_ATTRIBUTES,
SCST_C_DGRP_ATTRIBUTE_STATIC,
SCST_C_DGRP_SETATTR_FAIL);
}
sub setTargetGroupAttribute {
my $self = shift;
my $group = shift;
my $tgroup = shift;
my $attribute = shift;
my $value = shift;
return TRUE if !valid($group, $tgroup, $attribute) || !defined($value);
my $bytes = - ENOENT;
my $path = make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_TGROUPS, $tgroup, $attribute);
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $attribute = $value\n";
} else {
$value .= "\n";
$bytes = _syswrite($io, $value, length($value));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->deviceGroupExists($group);
return SCST_C_DEV_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetGroupExists($group, $tgroup);
return SCST_C_DGRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
return setAttrFailed($path, $bytes, SCST_C_TGRP_BAD_ATTRIBUTES,
SCST_C_TGRP_ATTRIBUTE_STATIC,
SCST_C_TGRP_SETATTR_FAIL);
}
sub setTargetGroupTargetAttribute {
my $self = shift;
my $group = shift;
my $tgroup = shift;
my $tgt = shift;
my $attribute = shift;
my $value = shift;
return TRUE if (!valid($group, $tgroup, $tgt, $attribute) ||
!defined($value));
my $bytes = - ENOENT;
my $path = make_path(SCST_DEV_GROUP_DIR(), $group, SCST_DG_TGROUPS,
$tgroup, $tgt, $attribute);
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $attribute = $value\n";
} else {
$value .= "\n";
$bytes = _syswrite($io, $value, length($value));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->deviceGroupExists($group);
return SCST_C_DEV_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetGroupExists($group, $tgroup);
return SCST_C_DGRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetGroupTargetExists($group, $tgroup, $tgt);
return SCST_C_TGRP_NO_TGT if (!$rc);
return $rc if ($rc > 1);
return setAttrFailed($path, $bytes, SCST_C_TGRP_TGT_BAD_ATTR,
SCST_C_TGRP_TGT_ATTR_STATIC,
SCST_C_TGRP_TGT_SETATTR_FAIL);
}
sub handlers {
my $self = shift;
my @handlers;
my $hHandle = new IO::Handle;
my $_path = SCST_HANDLERS_DIR();
if (!(opendir $hHandle, $_path)) {
return (undef, "handlers(): Unable to read directory '$_path': $!");
}
foreach my $handler (readdir($hHandle)) {
next if (($handler eq '.') || ($handler eq '..'));
if (-d make_path(SCST_HANDLERS_DIR(), $handler)) {
push @handlers, $handler;
}
}
close $hHandle;
@handlers = sort(@handlers);
return (\@handlers, undef);
}
sub handlerExists {
my $self = shift;
my $handler = shift;
return FALSE if (!valid($handler));
my ($handlers, $errorString) = $self->handlers();
return SCST_C_FATAL_ERROR if (!defined($handlers));
foreach my $_handler (@{$handlers}) {
return TRUE if ($handler eq $_handler);
}
return FALSE;
}
sub setHandlerAttribute {
my $self = shift;
my $handler = shift;
my $attribute = shift;
my $value = shift;
return TRUE if (!valid($attribute) || !defined($value));
my $bytes = - ENOENT;
my $path = make_path(SCST_HANDLERS_DIR(), $handler, $attribute);
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $path -> $attribute = $value\n";
} else {
$value .= "\n";
$bytes = _syswrite($io, $value, length($value));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->handlerExists($handler);
return SCST_C_HND_NO_HANDLER if (!$rc);
return $rc if ($rc > 1);
return setAttrFailed($path, $bytes, SCST_C_HND_BAD_ATTRIBUTES,
SCST_C_HND_ATTRIBUTE_STATIC,
SCST_C_HND_SETATTR_FAIL);
}
sub handlerAttributes {
my $self = shift;
my $handler = shift;
my %attributes = ( );
my $errorString;
my $a;
($a, $errorString) = devices($self, $handler);
$attributes{'devices'}->{'value'} = $a;
my $hHandle = new IO::Handle;
my $_path = make_path(SCST_HANDLERS_DIR(), $handler);
if (!(opendir $hHandle, $_path)) {
if ($self->handlerExists($handler) != TRUE) {
$errorString = "handlerAttributes(): Handler '$handler' is not available";
} else {
$errorString = "handlerAttributes(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
foreach my $attribute (readdir($hHandle)) {
next if ($attribute eq '.' || $attribute eq '..' ||
$attribute eq SCST_MGMT_IO || $attribute eq 'uevent' ||
$attribute eq 'module');
my $pPath = make_path(SCST_HANDLERS_DIR(), $handler, $attribute);
my $mode = (stat($pPath))[2];
my $is_static;
if (($mode & S_IWUSR) >> 6) {
$is_static = FALSE;
} else {
$is_static = TRUE;
}
my $path = make_path(SCST_HANDLERS_DIR(), $handler, $attribute);
my $io = new IO::File $path, O_RDONLY;
if (!$io) {
return (undef, "handlerAttributes(): Unable to read handler attribute ".
"'$attribute': $!");
}
my $value = <$io>;
$value = "" if (!defined($value));
chomp $value;
my $is_key = <$io>;
$is_key = $is_key && $is_key =~ /\[key\]/;
my $key = 0;
if ($is_key) {
if ($attribute =~ /^([^\d]+)(\d+)$/) {
$attribute = $1;
$key = $2;
}
}
if ($attribute eq SCST_MGMT_IO) {
$attributes{$attribute}->{'static'} = TRUE;
$attributes{$attribute}->{'value'} = $value;
next;
}
if (!(($mode & S_IRUSR) >> 6)) {
$attributes{$attribute}->{'static'} = FALSE;
$attributes{$attribute}->{'value'} = undef;
} elsif ($attribute eq SCST_TRACE_IO) {
$attributes{$attribute}->{'value'} = $value;
my @possible;
$value = '';
my $start = FALSE;
while (my $line = <$io>) {
$start = TRUE if ($line !~ /\[/);
$value .= $line if ($start);
}
$value =~ s/\n//g;
if ($value =~ /\[(.*)\]/) {
$value = $1;
foreach my $t (split(/\,/, $value)) {
$t =~ s/^\s+//; $t =~ s/\s+$//;
push @possible, $t;
}
}
$attributes{$attribute}->{'set'} = \@possible;
} elsif ($attribute eq 'type') {
my($type, $type_string) = split(/\s\-\s/, $value, 2);
$attributes{$attribute}->{'value'} = $type;
$attributes{'type_string'}->{'value'} = $type_string;
$attributes{'type_string'}->{'static'} = TRUE;
} else {
if ($is_key) {
$attributes{$attribute}->{'keys'}->{$key}->{'value'} = $value;
} else {
$attributes{$attribute}->{'value'} = $value;
}
}
$attributes{$attribute}->{'static'} = $is_static;
close $io;
}
close $hHandle;
$attributes{'devices'}->{'static'} = TRUE;
return (\%attributes, undef);
}
sub deviceExists {
my $self = shift;
my $device = shift;
my ($handlers, $errorString) = $self->handlers();
return SCST_C_FATAL_ERROR if (!defined($handlers));
foreach my $handler (@{$handlers}) {
return TRUE if $self->handlerDeviceExists($handler, $device);
}
return FALSE;
}
sub handlerDeviceExists {
my $self = shift;
my $handler = shift;
my $device = shift;
my ($devices, $errorString) = $self->devicesByHandler($handler);
if (!defined($devices)) {
my $rc = $self->handlerExists($handler);
return FALSE if (!$rc);
return $rc if ($rc > 1);
return SCST_C_FATAL_ERROR;
}
foreach my $_device (@{$devices}) {
return TRUE if ($_device eq $device);
}
return FALSE;
}
sub devicesByHandler {
my $self = shift;
my $handler = shift;
my $errorString;
my $attributes;
($attributes, $errorString) = $self->handlerAttributes($handler);
if (!defined($attributes)) {
if ($self->handlerExists($handler) != TRUE) {
$errorString = "devicesByHandler(): Handler '$handler' is not available";
} else {
$errorString = "devicesByHandler() failed";
}
return (undef, $errorString);
}
return (\@{$$attributes{'devices'}->{'value'}}, undef);
}
sub checkDeviceCreateAttributes {
my $self = shift;
my $handler = shift;
my $check = shift;
return FALSE if (!defined($check));
my ($available, $errorString) = $self->deviceCreateAttributes($handler);
if (!defined($available)) {
my $rc = $self->handlerExists($handler);
return SCST_C_HND_NO_HANDLER if (!$rc);
return $rc if (!$rc > 1);
return SCST_C_FATAL_ERROR;
}
if (ref($check) eq 'HASH') {
foreach my $attribute (keys %{$check}) {
if (!defined($$available{$attribute})) {
return TRUE;
}
}
} else {
if (!defined($$available{$check})) {
return TRUE;
}
}
return FALSE;
}
sub deviceCreateAttributes {
my $self = shift;
my $handler = shift;
my $available;
my %attributes = ( );
my $errorString;
return (undef, "missing handler argument") if (!valid($handler));
my $io = new IO::File make_path(SCST_HANDLERS_DIR(), $handler,
SCST_MGMT_IO), O_RDONLY;
if (!$io) {
if ($self->handlerExists($handler) != TRUE) {
$errorString = "deviceCreateAttributes(): Handler '$handler' ".
"is not available";
} else {
$errorString = "deviceCreateAttributes(): Unable to open mgmt ".
"interface for handler '$handler': $!";
}
return (undef, $errorString);
}
while (my $in = <$io>) {
if ($in =~ /^The following parameters available\:/) {
(undef, $available) = split(/\:/, $in, 2);
$available =~ s/\.$//;
}
}
close $io;
if ($available) {
foreach my $attribute (split(/\,/, $available)) {
$attribute =~ s/^\s+//;
$attribute =~ s/\s+$//;
$attributes{$attribute} = '';
}
}
return (\%attributes, undef);
}
sub openDevice {
my $self = shift;
my $handler = shift;
my $device = shift;
my $attributes = shift;
return SCST_C_DEV_OPEN_FAIL
if (!valid($handler, $device, $attributes));
my $o_string = "";
foreach my $attribute (keys %{$attributes}) {
my $value = $$attributes{$attribute};
$o_string .= "$attribute=$value;";
}
$o_string =~ s/\s$//;
my ($path, $cmd);
$path = make_path(SCST_HANDLERS_DIR(), $handler, SCST_MGMT_IO);
$cmd .= "add_device $device $o_string";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $errno = $!;
my $rc = $self->handlerExists($handler);
return SCST_C_HND_NO_HANDLER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->checkDeviceCreateAttributes($handler, $attributes);
return SCST_C_DEV_BAD_ATTRIBUTES if ($rc == TRUE);
return $rc if ($rc > 1);
$rc = $self->handlerDeviceExists($handler, $device);
return SCST_C_DEV_EXISTS if ($rc == TRUE);
return $rc if ($rc > 1);
return SCST_C_DEV_OPEN_FAIL if ($errno == EINVAL);
return SCST_C_FATAL_ERROR;
}
sub closeDevice {
my $self = shift;
my $handler = shift;
my $device = shift;
return SCST_C_DEV_CLOSE_FAIL if (!valid($handler, $device));
my ($path, $cmd);
$path = make_path(SCST_HANDLERS_DIR(), $handler, SCST_MGMT_IO);
$cmd .= "del_device $device";
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $errno = $!;
my $rc = $self->handlerExists($handler);
return SCST_C_HND_NO_HANDLER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->handlerDeviceExists($handler, $device);
return SCST_C_DEV_NO_DEVICE if ($rc != TRUE);
return $rc if ($rc > 1);
return SCST_C_FATAL_ERROR;
}
sub setDeviceAttribute {
my $self = shift;
my $device = shift;
my $attribute = shift;
my $value = shift;
my ($path, $cmd);
$path = make_path(SCST_DEVICES_DIR(), $device, $attribute);
$cmd .= $value;
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
if ($self->{'debug'}) {
print "DBG($$): $cmd > $path\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
my $rc = $self->deviceOpen($device);
return SCST_C_DEV_NO_DEVICE if (!$rc);
return $rc if ($rc > 1);
return setAttrFailed($path, $bytes, SCST_C_DEV_BAD_ATTRIBUTES,
SCST_C_DEV_ATTRIBUTE_STATIC,
SCST_C_DEV_SETATTR_FAIL);
}
sub checkTargetCreateAttributes {
my $self = shift;
my $driver = shift;
my $check = shift;
return FALSE if (!defined($check));
my ($available, $errorString) = $self->targetCreateAttributes($driver);
if (!defined($available)) {
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
return SCST_C_FATAL_ERROR;
}
if (ref($check) eq 'HASH') {
foreach my $attribute (keys %{$check}) {
if (!defined($$available{$attribute})) {
return TRUE;
}
}
} else {
if (!defined($$available{$check})) {
return TRUE;
}
}
return FALSE;
}
sub targetCreateAttributes {
my $self = shift;
my $driver = shift;
my $available;
my %attributes = ( );
my $errorString;
my $io = new IO::File make_path(SCST_TARGETS_DIR(), $driver,
SCST_MGMT_IO), O_RDONLY;
if (!$io) {
if ($self->driverExists($driver) != TRUE) {
$errorString = "targetCreateAttributes(): Driver '$driver' ".
"is not available";
} else {
$errorString = "targetCreateAttributes(): Unable to open driver mgmt ".
"interface for driver '$driver': $!";
}
return (undef, $errorString);
}
while (my $in = <$io>) {
if ($in =~ /^The following parameters available\:/) {
(undef, $available) = split(/\:/, $in, 2);
$available =~ s/\.$//;
}
}
close $io;
if ($available) {
foreach my $attribute (split(/\,/, $available)) {
$attribute =~ s/^\s+//;
$attribute =~ s/\s+$//;
$attributes{$attribute} = '';
}
}
return (\%attributes, undef);
}
sub enableTarget {
my $self = shift;
my $driver = shift;
my $target = shift;
my $enable = shift;
$enable = TRUE if ($enable);
return $self->setTargetAttribute($driver, $target, 'enabled', $enable);
}
sub resyncDevice {
my $self = shift;
my $device = shift;
return $self->setDeviceAttribute($device, 'resync_size', 1);
}
sub setT10DeviceId {
my $self = shift;
my $device = shift;
my $t10_id = shift;
return $self->setDeviceAttribute($device, 't10_dev_id', $t10_id);
}
sub deviceGroupExists {
my $self = shift;
my $group = shift;
my ($groups, $errorString) = $self->deviceGroups();
return SCST_C_FATAL_ERROR if (!defined($groups));
foreach my $_group (@{$groups}) {
return TRUE if ($group eq $_group);
}
return FALSE;
}
sub deviceGroupDeviceExists {
my $self = shift;
my $group = shift;
my $device = shift;
my ($devices, $errorString) = $self->deviceGroupDevices($group);
if (!defined($devices)) {
return SCST_C_FATAL_ERROR;
}
foreach my $_device (@{$devices}) {
return TRUE if ($device eq $_device);
}
return FALSE;
}
sub targetGroupExists {
my $self = shift;
my $group = shift;
my $tgroup = shift;
my ($tgroups, $errorString) = $self->targetGroups($group);
if (!defined($tgroups)) {
return SCST_C_FATAL_ERROR;
}
foreach my $_tgroup (@{$tgroups}) {
return TRUE if ($tgroup eq $_tgroup);
}
return FALSE;
}
sub targetGroupTargetExists {
my $self = shift;
my $group = shift;
my $tgroup = shift;
my $tgt = shift;
my ($targets, $errorString) = $self->targetGroupTargets($group, $tgroup);
if (!defined($targets)) {
return SCST_C_FATAL_ERROR;
}
foreach my $_tgt (@{$targets}) {
return TRUE if ($tgt eq $_tgt);
}
return FALSE;
}
sub checkLunCreateAttributes {
my $self = shift;
my $driver = shift;
my $target = shift;
my $check = shift;
my $group = shift;
return FALSE if (!defined($check));
my ($available, $errorString) = $self->lunCreateAttributes($driver, $target, $group);
if (!defined($available)) {
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
if ($group) {
$rc = $self->groupExists($driver, $target, $group);
return SCST_C_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
}
return SCST_C_FATAL_ERROR;
}
if (ref($check) eq 'HASH') {
foreach my $attribute (keys %{$check}) {
if (!defined($$available{$attribute})) {
return TRUE;
}
}
} else {
if (!defined($$available{$check})) {
return TRUE;
}
}
return FALSE;
}
sub lunCreateAttributes {
my $self = shift;
my $driver = shift;
my $target = shift;
my $group = shift;
my $available;
my %attributes = ( );
my $errorString;
my $_path;
if (valid($group)) {
$_path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_GROUPS, $group, SCST_LUNS,
SCST_MGMT_IO);
} else {
$_path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_LUNS, SCST_MGMT_IO);
}
my $io = new IO::File $_path, O_RDONLY;
if (!$io) {
if ($self->driverExists($driver) != TRUE) {
$errorString = "lunCreateAttributes(): Driver '$driver' ".
"is not available";
} elsif ($self->targetExists($driver, $target) != TRUE) {
$errorString = "lunCreateAttributes(): Target '$target' ".
"is not available";
} elsif (valid($group) &&
$self->groupExists($driver, $target, $group) != TRUE) {
$errorString = "lunCreateAttributes(): Group '$group' ".
"does not exist";
} else {
$errorString = "lunCreateAttributes(): Unable to open luns mgmt ".
"interface for group '$group': $!";
}
return (undef, $errorString);
}
while (my $in = <$io>) {
if ($in =~ /^The following parameters available\:/) {
(undef, $available) = split(/\:/, $in, 2);
$available =~ s/\.$//;
}
}
close $io;
if ($available) {
foreach my $attribute (split(/\,/, $available)) {
$attribute =~ s/^\s+//;
$attribute =~ s/\s+$//;
$attributes{$attribute} = '';
}
}
return (\%attributes, undef);
}
sub checkInitiatorCreateAttributes {
my $self = shift;
my $driver = shift;
my $target = shift;
my $group = shift;
my $check = shift;
return FALSE if (!defined($check));
my ($available, $errorString) = $self->initiatorCreateAttributes($driver, $target, $group);
if (!defined($available)) {
my $rc = $self->driverExists($driver);
return SCST_C_DRV_NO_DRIVER if (!$rc);
return $rc if ($rc > 1);
$rc = $self->targetExists($driver, $target);
return SCST_C_TGT_NO_TARGET if (!$rc);
return $rc if ($rc > 1);
$rc = $self->groupExists($driver, $target, $group);
return SCST_C_GRP_NO_GROUP if (!$rc);
return $rc if ($rc > 1);
return SCST_C_FATAL_ERROR;
}
if (ref($check) eq 'HASH') {
foreach my $attribute (keys %{$check}) {
if (!defined($$available{$attribute})) {
return TRUE;
}
}
} else {
if (!defined($$available{$check})) {
return TRUE;
}
}
return FALSE;
}
sub initiatorCreateAttributes {
my $self = shift;
my $driver = shift;
my $target = shift;
my $group = shift;
my $available;
my %attributes = ( );
my $errorString;
my $io = new IO::File make_path(SCST_TARGETS_DIR(), $driver,
$target, SCST_GROUPS, $group,
SCST_INITIATORS, SCST_MGMT_IO),
O_RDONLY;
if (!$io) {
if ($self->driverExists($driver) != TRUE) {
$errorString = "initiatorCreateAttributes(): Driver '$driver' ".
"is not available";
} elsif ($self->targetExists($driver, $target) != TRUE) {
$errorString = "initiatorCreateAttributes(): Target '$target' ".
"is not available";
} elsif ($self->groupExists($driver, $target, $group) != TRUE) {
$errorString = "initiatorCreateAttributes(): Group '$group' ".
"does not exist";
} else {
$errorString = "initiatorCreateAttributes(): Unable to open initiators mgmt ".
"interface for group '$group': $!";
}
return (undef, $errorString);
}
while (my $in = <$io>) {
if ($in =~ /^The following parameters available\:/) {
(undef, $available) = split(/\:/, $in, 2);
$available =~ s/\.$//;
}
}
close $io;
if ($available) {
foreach my $attribute (split(/\,/, $available)) {
$attribute =~ s/^\s+//;
$attribute =~ s/\s+$//;
$attributes{$attribute} = '';
}
}
return (\%attributes, undef);
}
sub sessions {
my $self = shift;
my $driver = shift;
my $target = shift;
my %_sessions;
my $errorString;
return (undef, "Too few arguments") if (!valid($driver, $target));
my $sHandle = new IO::Handle;
my $_path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_SESSIONS);
if (!(opendir $sHandle, $_path)) {
if ($self->driverExists($driver) != TRUE) {
$errorString = "sessions(): Driver '$driver' ".
"is not available";
} elsif ($self->targetExists($driver, $target) != TRUE) {
$errorString = "sessions(): Target '$target' ".
"is not available";
} else {
$errorString = "sessions(): Unable to read directory '$_path': $!";
}
return (undef, $errorString);
}
foreach my $session (readdir($sHandle)) {
next if (($session eq '.') || ($session eq '..'));
my $pHandle = new IO::Handle;
my $sPath = make_path($_path, $session);
if (!(opendir $pHandle, $sPath)) {
return (undef, "sessions(): Unable to read directory '$_path': $!");
}
foreach my $attribute (readdir($pHandle)) {
next if ($attribute eq '.' || $attribute eq '..' ||
$attribute eq SCST_MGMT_IO);
my $pPath = make_path($sPath, $attribute);
if ($attribute eq 'luns') {
my $linked = readlink $pPath;
my $g = SCST_GROUPS;
my $l = SCST_LUNS;
if (defined($linked) &&
$linked =~ /\.\.\/\.\.\/$g\/(.*)\/$l/) {
my $group = $1;
$_sessions{$session}->{$attribute} =
$self->luns($driver, $target, $group);
}
} else {
my $mode = (stat($pPath))[2];
if (!$mode) {
$mode = 0;
}
if (-d $pPath) {
# Skip directories
} else {
if (!(($mode & S_IRUSR) >> 6)) {
$_sessions{$session}->{$attribute}->{'static'} = FALSE;
$_sessions{$session}->{$attribute}->{'value'} = undef;
} else {
my $is_static;
if (($mode & S_IWUSR) >> 6) {
$is_static = FALSE;
} else {
$is_static = TRUE;
}
my $io = new IO::File $pPath, O_RDONLY;
if (!$io) {
return (undef,
"sessions(): Unable to read ".
"session attribute '$attribute': $!");
}
my $value = <$io>;
$value = "" if (!defined($value));
chomp $value;
close $io;
$_sessions{$session}->{$attribute}->{'value'} = $value;
$_sessions{$session}->{$attribute}->{'static'} = $is_static;
}
}
}
}
close $pHandle;
}
close $sHandle;
return (\%_sessions, undef);
}
sub closeSession {
my $self = shift;
my $driver = shift;
my $target = shift;
my $session = shift;
my ($sessions, $errorString) = $self->sessions($driver, $target);
return SCST_C_NO_SESSION if (!defined($$sessions{$session}));
# If it's not closable, silently return
return FALSE if (!defined($$sessions{$session}->{'force_close'}));
my $path = make_path(SCST_TARGETS_DIR(), $driver, $target,
SCST_SESSIONS, $session, 'force_close');
my $bytes = - ENOENT;
my $io = new IO::File $path, O_WRONLY;
if ($io) {
my $cmd = "1";
if ($self->{'debug'}) {
print "DBG($$): $cmd\n";
} else {
$bytes = _syswrite($io, $cmd, length($cmd));
}
close $io;
return FALSE if ($self->{'debug'} || $bytes > 0);
}
return SCST_C_SESSION_CLOSE_FAIL;
}
sub sgvStats {
my $self = shift;
my %stats;
my $sHandle = new IO::Handle;
my $_path = SCST_SGV_DIR();
if (!(opendir $sHandle, $_path)) {
return (undef, "svgStats(): Unable to read directory '$_path': $!");
}
foreach my $stat (readdir($sHandle)) {
next if (($stat eq '.') || ($stat eq '..'));
my $sPath = make_path(SCST_SGV_DIR(), $stat);
if (-d $sPath) {
my $lHandle = new IO::Handle;
if (!(opendir $lHandle, $sPath)) {
return (undef, "svgStats(): Unable to read directory '$sPath': $!");
}
foreach my $lief (readdir($lHandle)) {
my $pPath = make_path($sPath, $lief);
my $io = new IO::File $pPath, O_RDONLY;
if (!$io) {
return (undef, "sgvStats(): Unable to read ".
"sgv stat '$stat/$lief': $!");
}
my $buffer;
while (my $value = <$io>) {
$buffer .= $value;
}
close $io;
$stats{$stat}->{$lief} = $buffer;
}
close $lHandle;
} else {
my $io = new IO::File $sPath, O_RDONLY;
if (!$io) {
return (undef, "sgvStats(): Unable to read ".
"sgv stat '$stat': $!");
}
my $buffer;
while (my $value = <$io>) {
$buffer .= $value;
}
close $io;
$stats{$stat} = $buffer;
}
}
close $sHandle;
return (\%stats, undef);
}
sub errorString {
my $self = shift;
my $rc = shift;
return defined($rc) ? $VERBOSE_ERROR{$rc} : undef;
}
# Read from the SCST sysfs file $1. Return either the data read or undef if
# reading failed.
sub _sysread {
my $io = shift;
my $deadline = time() + $TIMEOUT;
my $result;
while (time() < $deadline) {
my $bytes = sysread($io, $result, 4096);
last if (defined($bytes) || $! != EAGAIN);
sleep 1;
}
return $result;
}
# Write the first $3 bytes of $2 into the SCST sysfs file $1. Return either
# the number of bytes written or -errno if writing failed.
sub _syswrite {
my $io = shift;
my $cmd = shift;
my $length = shift;
my $now = time();
my $res_file = SCST_QUEUE_RES_PATH();
my $bytes = syswrite($io, $cmd, $length);
$bytes = -$! if (!defined($bytes));
if (defined($res_file) && $bytes == - EAGAIN) {
my $res_io = new IO::File $res_file, O_RDONLY;
if (!$res_io) {
cluck("FATAL: Failed opening $res_file: $!");
return - ENOENT;
}
my $res_val;
while (($now + $TIMEOUT) > time()) {
if (!defined(sysread($res_io, $res_val, 8)) &&
$! == EAGAIN) {
sleep 1;
} else {
last;
}
}
close $res_io;
if (!defined($res_val)) {
my $_cmd = $cmd; chomp $_cmd;
cluck("Timeout while waiting for command '$_cmd' to complete");
} elsif ($res_val == 0) {
$bytes = $length;
}
}
return $bytes;
}
sub make_path {
return File::Spec->catdir(@_);
}
;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";
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
=back
=head1 WARNING
None at this time.
=head1 NOTES
None at this time.
=cut