tgt/tgt-0.9.0-update

1910 lines
58 KiB
Plaintext

diff --git a/README b/README
index dd99e74..22785fc 100644
--- a/README
+++ b/README
@@ -3,46 +3,53 @@ Introduction
Linux target framework (tgt) aims to simplify various SCSI target
driver (iSCSI, Fibre Channel, SRP, etc) creation and maintenance.
-Tgt consists of kernel modules, user-space daemon, and user-space
-tools. Some target drivers uses all of them and some use only
-user-space daemon and tools (i.e. they completely runs in user space).
+Currently, tgt supports the following target drivers:
-Currently, tgt supports three target drivers:
+- iSCSI software target driver for Ethernet NICs
-- IBM VIO server (ibmvstgt)
-- iSCSI
-- Xen vscsifront/back
+- iSER software target driver for Infiniband and RDMA NICs
-Note that tgt is under active development. Don't play with important
-data.
+- IBM System p VIO server
-The code is under the GNU General Public License version 2.
+- FCoE software target driver for Ethernet NICs (in progress)
+- Qlogic qla2xxx FC target driver (in progress)
-Preparation
--------------
-The iSCSI target driver can works with the 2.6.X kernels. It requires
-OpenSSL library (libssl-dev for debian, openssl-devel for Fedora).
+Tgt consists of kernel modules, user-space daemon, and user-space
+tools. iSCSI, iSER, and FCoE target drivers use only user-space daemon
+and tools (i.e. they are just user-space applications. They don't need
+any kernel support).
+
+tgt can emulate the following device types:
-host:~/tgt/usr$ make ISCSI=1
+- SBC: a virtual disk drive that can use a file to store the content.
-If you want IBM VIO target driver, get kernel version 2.6.20, rebuild
-the kernel, and reboot with the new kernel. Note you need to enable
-SCSI_TGT, SCSI_SRP, and SCSI_IBMVSCSIS kernel options.
+- SMC: a virtual media jukebox that can be controlled by the "mtx"
+tool (partially functional).
-host:~/tgt/usr$ make KERNELSRC=<kernel-src-directory> IBMVIO=1
+- MMC: a virtual DVD drive that can read DVD-ROM iso files and create
+burnable DVD+R. It can be combined with SMC to provide a fully
+operational DVD jukebox.
-Make sure that everything is built successfully.
+- SSC: a virtual tape device (aka VTL) that can use a file to store
+the content (in progress).
-Now you can run tgt. Target drivers have their own ways for
-configuration. So find an appropriate documentation in the doc
-directory.
+- OSD: a virtual object-based storage device that can use a file to
+store the content (in progress).
+
+The code is under the GNU General Public License version 2.
+
+
+Preparation
+-------------
+Target drivers have their own ways to build, configure, etc. So find
+an appropriate documentation in the doc directory.
Developer Notes
-------------
The central resource for tgt development is the mailing list
-(stgt-devel@lists.berlios.de).
+(stgt@vger.kernel.org).
First, please read the following documents (in short, follow Linux
kernel development rules):
diff --git a/doc/README.ibmvstgt b/doc/README.ibmvstgt
index 9a9dbd8..d77f932 100644
--- a/doc/README.ibmvstgt
+++ b/doc/README.ibmvstgt
@@ -1,5 +1,13 @@
Starting
-------------
+If you want IBM VIO target driver, get kernel version 2.6.20, rebuild
+the kernel, and reboot with the new kernel. Note you need to enable
+SCSI_TGT, SCSI_SRP, and SCSI_IBMVSCSIS kernel options.
+
+host:~/tgt/usr$ make KERNELSRC=<kernel-src-directory> IBMVIO=1
+
+Make sure that everything is built successfully.
+
Try the following commands:
host:~/tgt$ su
diff --git a/doc/README.iscsi b/doc/README.iscsi
index 5aec190..e6b4d09 100644
--- a/doc/README.iscsi
+++ b/doc/README.iscsi
@@ -5,6 +5,13 @@ This show a simple example to set up some targets.
Starting the daemon
-------------
+The iSCSI target driver works with the 2.6.X kernels. It requires
+OpenSSL library (libssl-dev for debian, openssl-devel for Fedora).
+
+First, you need to compile the source code:
+
+host:~/tgt/usr$ make ISCSI=1
+
Try the following commands:
host:~/tgt$ su
diff --git a/doc/targets.conf.example b/doc/targets.conf.example
new file mode 100644
index 0000000..46be8fe
--- /dev/null
+++ b/doc/targets.conf.example
@@ -0,0 +1,168 @@
+# This is a sample config file for tgt-admin.
+# By default, tgt-admin looks for its config file in /etc/tgt/targets.conf
+
+# This one includes other config files:
+
+include /etc/tgt/temp/*.conf
+
+
+# Set the driver. If not specified, defaults to "iscsi".
+
+default-driver iscsi
+
+
+# Sample target with one LUN only. Defaults to allow access for all initiators:
+
+<target iqn.2008-09.com.example:server.target1>
+ backing-store /dev/LVM/somedevice
+</target>
+
+
+# Similar, but we use "direct-store" instead of "backing-store".
+# "direct-store" reads drive parameters with sg_inq command and sets them to
+# the target.
+# Parameters fatched with sg_inq are:
+# - Vendor identification
+# - Product identification
+# - Product revision level
+# - Unit serial number (if present)
+# We also specify "incominguser".
+
+<target iqn.2008-09.com.example:server.target2>
+ direct-store /dev/sdd
+ incominguser someuser secretpass12
+</target>
+
+
+# An example with multiple LUNs, disabled write-cache (tgtd enables write-cache
+# by default) and vendor identification set to "MyVendor"
+
+<target iqn.2008-09.com.example:server.target3>
+ backing-store /dev/LVM/somedevice1 # Becomes LUN 1
+ backing-store /dev/LVM/somedevice2 # Becomes LUN 2
+ backing-store /dev/LVM/somedevice3 # Becomes LUN 3
+ write-cache off
+ vendor_id MyCompany Inc.
+</target>
+
+
+# Similar to the one above, but we fetch vendor_id, product_id, product_rev and
+# scsi_sn from the disks.
+# Vendor identification (vendor_id) is replaced in all disks by "MyVendor"
+
+<target iqn.2008-09.com.example:server.target4>
+ direct-store /dev/sdb # Becomes LUN 1
+ direct-store /dev/sdc # Becomes LUN 2
+ direct-store /dev/sdd # Becomes LUN 3
+ write-cache off
+ vendor_id MyCompany Inc.
+</target>
+
+
+# Note that "first-device-first-lun numbering" will work only for simple
+# scenarios above, where _only_ direct-store _or_ backing-store is used.
+# If you mix backing-store and direct-store, then all backing-store entries
+# are processed before direct-store-entries.
+
+<target iqn.2008-09.com.example:server.target4>
+ direct-store /dev/sdb # Becomes LUN 3
+ backing-store /dev/sdc # Becomes LUN 1
+ direct-store /dev/sdd # Becomes LUN 4
+ backing-store /dev/sde # Becomes LUN 2
+</target>
+
+
+# Even more complicated example - each device has different parameters.
+# You can use indentation to make the config file more readable.
+# Note that LUNs will be assigned more or less randomly here (and still
+# backing-store get LUNs assigned before drect-store).
+# You can specify multiple mode_page parameters (they are commented out
+# in this example).
+# Note that some parameters (write-cache, scsi_sn) were specified "globally".
+# "Global" parameters will be applied to all LUNs; they can be overwritten
+# "locally", per LUN.
+
+<target iqn.2008-09.com.example:server.target5>
+
+ <direct-store /dev/sdd>
+ vendor_id VENDOR1
+ removable 1
+ device-type cd
+ # lun 1 # Not yet supported
+ </direct-store>
+
+ <direct-store /dev/sda>
+ vendor_id VENDOR2
+ # lun 2 # Not yet supported
+ </direct-store>
+
+ <backing-store /dev/sdb1>
+ vendor_id back1
+ scsi_sn SERIAL
+ write-cache on
+ # lun 3 # Not yet supported
+ </backing-store>
+
+ <backing-store /dev/sdd1>
+ vendor_id back2
+ #mode_page 8:0:18:0x10:0:0xff....
+ #mode_page 8:0:18:0x10:0:0xff....
+ # lun 4 # Not yet supported
+ </backing-store>
+
+ # Some more parameters which can be specified locally or globally:
+ #scsi_id ...
+ #scsi_sn ...
+ #vendor_id ...
+ #product_id ...
+ #product_rev ...
+ #sense_format ...
+ #removable ...
+ #online ...
+ #path ...
+ #mode_page 8:0:18:0x10:0:0xff....
+ #mode_page 8:0:18:0x10:0:0xff....
+ #device-type
+
+ write-cache off
+ scsi_sn multipath-10
+
+ # Parameters below are global. They can't be configured per LUN.
+ # Only allow connections from 192.168.100.1 and 192.168.200.5
+ initiator-address 192.168.100.1
+ initiator-address 192.168.200.5
+
+ # Allowed incoming users
+ incominguser user1 secretpass12
+ incominguser user2 secretpass23
+
+ # Outgoing user
+ outgoinguser userA secretpassA
+
+</target>
+
+
+
+# Not supported configurations, and therefore, commented out:
+
+#<target iqn.2008-09.com.example:server.target6>
+# <direct-store /dev/sdd>
+# vendor_id VENDOR1
+# </direct-store>
+#
+# direct-store /dev/sdc
+#</target>
+
+# This one will break the parser:
+
+#<target iqn.2008-09.com.example:server.target7>
+# <direct-store /dev/sdd>
+# vendor_id VENDOR1
+# </direct-store>
+#
+# direct-store /dev/sdc
+#
+# <direct-store /dev/sdd>
+# vendor_id VENDOR1
+# </direct-store>
+#</target>
diff --git a/scripts/tgt-admin b/scripts/tgt-admin
index fe95723..e4be373 100755
--- a/scripts/tgt-admin
+++ b/scripts/tgt-admin
@@ -11,7 +11,6 @@
use strict;
use Config::General qw(ParseConfig);
-use Data::Dumper;
use Getopt::Long;
# Our config file
@@ -30,12 +29,15 @@ This tool configures tgt targets.
(see "--offline help" for more info)
--ready <value> put all or selected targets in ready state
(see "--ready help" for more info)
+ --update <value> update configuration for all or selected targets
+ (see "--update help" for more info)
-s, --show show all the targets
-c, --conf <conf file> specify an alternative configuration file
--ignore-errors continue even if tgtadm exits with non-zero code
-f, --force force some operations even if the target is in use
-p, --pretend only print tgtadm options
- --dump dump current tgtd configuration
+ --dump dump current tgtd configuration (note: does not
+ include detailed parameters, like write caching)
-v, --verbose increase verbosity (show tgtadm commands)
-h, --help show this help
@@ -49,6 +51,7 @@ my $execute = 0;
my $delete = 0;
my $offline = 0;
my $ready = 0;
+my $update = 0;
my $show = 0;
my $alternate_conf="0";
my $ignore_errors = 0;
@@ -62,6 +65,7 @@ my $result = GetOptions (
"delete=s" => \$delete,
"offline=s" => \$offline,
"ready=s" => \$ready,
+ "update=s" => \$update,
"s|show" => \$show,
"c|conf=s" => \$alternate_conf,
"ignore-errors" => \$ignore_errors,
@@ -73,7 +77,7 @@ my $result = GetOptions (
);
if (($help == 1) || ($param eq undef)) {
- &usage
+ usage;
}
# Show all the targets and exit
@@ -99,7 +103,6 @@ sub process_targets {
my @show_target = `tgtadm --op show --mode target`;
my $tid;
my $targetname;
-
# Here, we create hashes of target names (all target data) and target tids
foreach my $show_target_line (@show_target) {
if ( $show_target_line =~ m/^Target (\d*): (.+)/ ) {
@@ -128,7 +131,7 @@ sub parse_configs {
%conf = ParseConfig(-ConfigFile => "$alternate_conf", -UseApacheInclude => 1, -IncludeGlob => 1,);
}
else {
- die("file $alternate_conf not found. Exiting...\n");
+ die("Config file $alternate_conf not found. Exiting...\n");
}
} else {
# Parse the config file with Config::General
@@ -145,13 +148,17 @@ my $default_driver;
my $target;
my $option;
my $value;
+my $lun;
sub add_targets {
-
+ my $single_target = $_[0];
+ my $configured = $_[1];
+ my $connected = $_[2];
+ my $in_configfile = $_[3];
foreach my $k (sort keys %conf) {
- if ( $k eq "default-driver" ) {
- if ( not length ref($conf{$k}) ) {
+ if ($k eq "default-driver") {
+ if (not length ref($conf{$k})) {
$default_driver = $conf{$k};
} else {
print "Multiple default-driver definitions are not allowed!\n";
@@ -162,134 +169,344 @@ sub add_targets {
}
# If $default_driver is empty, default to iscsi
- if ( not defined $default_driver ) {
+ if (not defined $default_driver) {
execute("# default-driver not defined, defaulting to iscsi.\n");
$default_driver = "iscsi";
}
foreach my $k (sort keys %conf) {
- if ( $k eq "target" ) {
+ if ($k eq "target") {
foreach my $k2 (sort keys %{$conf{$k}}) {
- $target = $k2;
- my $allowall = 1;
- if ( not defined $tgtadm_output{$k2} ) {
- # We have to find available tid
- $next_tid = $next_tid + 1;
+ # Do we run update or execute?
+ if (length $single_target) {
+ if ($single_target ne $k2) {
+ next;
+ } else {
+ $target = $single_target;
+ }
+ } else {
+ $target = $k2;
}
- else {
- execute("# Target $target already exist!");
- execute("# Updating Target $target");
- execute("tgtadm --op update --mode target --tid=$next_tid -n state -v offline");
- execute("tgtadm --mode target --op delete --tid=$next_tid");
+
+ my $in_use = 0;
+ if (length $single_target) {
+ $in_use = main_delete($target);
}
+ my $allowall = 1;
+ if ((not defined $tgtadm_output{$k2}) ||
+ ($update ne 0 && $in_use == 0) ||
+ ($update ne 0 && $in_use == 1 && $pretend == 1 && $force == 1))
+ {
+ # We have to find available tid
+ if ($in_configfile == 1 && $configured == 0 && $pretend == 0) {
+ my $maxtid = find_max_tid();
+ $next_tid = $maxtid + 1;
+ } elsif (length $single_target && $configured == 1) {
+ $next_tid = $tgtadm_output_tid{$target};
+ } else {
+ $next_tid = $next_tid + 1;
+ }
+
+ # Before we add a target, we need to know its type
+ # and other parameters which can be specified globally
+ my %target_options;
+ my $target_options_ref;
+ foreach my $k3 (sort keys %{$conf{$k}{$k2}}) {
+ $lun = 1;
+ $option = $k3;
+ $value = $conf{$k}{$k2}{$k3};
+ check_value($value);
+ $target_options{$option} = $value;
+ $target_options_ref = \%target_options;
+ }
- # Before we add a target, we need to know its type
- my $driver;
- foreach my $k3 (sort keys %{$conf{$k}{$k2}}) {
- $option = $k3;
- $value = $conf{$k}{$k2}{$k3};
- &check($value);
- if ( $option eq "driver" ) {
- if (ref($value) eq "ARRAY") {
- print "Multiple driver definitions not allowed!\n";
- print "Check your config file for errors (target: $target).\n";
- exit 1;
+ if (not defined $target_options{"driver"}) {
+ $target_options{"driver"} = $default_driver;
+ }
+ my $driver = $target_options{"driver"};
+ execute("# Adding target: $target");
+ execute("tgtadm --lld $driver --op new --mode target --tid $next_tid -T $target");
+ foreach my $k3 (sort keys %{$conf{$k}{$k2}}) {
+ $option = $k3;
+ $value = $conf{$k}{$k2}{$k3};
+ check_value($value);
+ process_options($target_options_ref);
+ # If there was no option called "initiator-address", it means
+ # we want to allow ALL initiators for this target
+ if ($option eq "initiator-address") {
+ $allowall = 0;
}
- $driver = $value;
+ }
+
+ if ($allowall == 1) {
+ execute("tgtadm --lld $driver --op bind --mode target --tid $next_tid -I ALL");
+ }
+
+ } else {
+ if (not length $configured || $in_use eq 1) {
+ execute("# Target $target already exists!");
}
}
+ }
+ if (length $single_target && $in_configfile == 0 && $configured == 0) {
+ print "Target $single_target is currently not configured\n";
+ print "and does not exist in the config file - can't continue!\n";
+ exit 1;
+ }
+ execute();
+ }
+ }
+}
- if ( not defined $driver ) {
- $driver = $default_driver;
+# Some options can be specified only once
+sub check_if_hash_array {
+ my $check = $_[0];
+ my $definition = $_[1];
+ if (ref($check) eq 'ARRAY' || ref($check) eq "HASH") {
+ print "Multiple '$definition' definitions in '$option' not allowed!\n";
+ print "Check your config file for errors (target: $target).\n";
+ exit 1;
+ }
+}
+
+# Force an array if we just have one command
+sub force_array {
+ unless (ref($value) eq 'ARRAY') {
+ $value = [ $value ];
+ }
+}
+
+# If we start any external command, we want to know if it exists
+sub check_exe {
+ my $command = $_[0];
+ my $option = $_[1];
+ my @path = split(":", $ENV{PATH});
+ my $exists = 0;
+ foreach my $path (@path) {
+ if ( -x "$path/$command" && -f "$path/$command" ) { $exists = 1 }
+ }
+ if ( $exists == 0 ) {
+ print "Command $command (needed by $option option in your config file) is not in your path - can't continue!\n";
+ exit 1;
+ }
+}
+
+# Apply additional parameters
+sub add_params {
+ my $param = shift;
+ my $param_value = shift;
+ my $lun = shift;
+ my $driver = shift;
+
+ if ($param eq "write-cache") {
+ if ($param_value eq "off") {
+ return("tgtadm --lld $driver --op update --mode logicalunit --tid $next_tid --lun=$lun --params mode_page=8:0:18:0x10:0:0xff:0xff:0:0:0xff:0xff:0xff:0xff:0x80:0x14:0:0:0:0:0:0");
+ } elsif ($param_value eq "on" || not length $param_value) {
+ return("# Write cache is enabled (default) for lun $lun.");
+ } else {
+ return("# WARNING! Unknown value ($param_value) to write-cache! Accepted values are \"on\" and \"off\".");
+ }
+ }
+
+ if ($param eq "scsi_id" || $param eq "scsi_sn" || $param eq "vendor_id" || $param eq "product_id" ||
+ $param eq "product_rev" || $param eq "sense_format" || $param eq "removable" || $param eq "online" ||
+ $param eq "path" || $param eq "mode_page") {
+ return("tgtadm --lld $driver --op update --mode logicalunit --tid $next_tid --lun=$lun --params $param=\"$param_value\"");
+ }
+}
+
+# Add backing or direct store
+sub add_backing_direct {
+ my $backing_store = $_[0];
+ my $target_options_ref = $_[1];
+ my $lun = $_[2];
+ my $direct_store = $_[3];
+ my $driver = $$target_options_ref{"driver"};
+
+ # Is the device in use?
+ (my $can_alloc, my $dev) = check_device($backing_store);
+
+ # Needed if the config file has mixed definitions
+ if (ref($backing_store) eq "HASH") {
+ foreach my $backing_store (sort keys %$value) {
+ add_backing_direct($backing_store,$target_options_ref,$lun,$direct_store);
+ $lun += 1;
+ }
+ return $lun;
+ } elsif (-e $backing_store && $can_alloc == 1) {
+ my @exec_commands;
+ my $device_type;
+ # Process parameters for each lun / backing store
+ if (ref $value eq "HASH") {
+ my %params_added;
+ my @mode_page;
+ foreach my $store (keys %$value) {
+ if (ref $$value{$store} eq "HASH" && $store eq $backing_store) {
+ foreach my $store_option (keys %{$$value{$store}}) {
+ my $result = $$value{$store}{$store_option};
+ check_value($result);
+ if ($store_option ne "mode_page") { check_if_hash_array($result,$store_option) }
+ # write-cache can be set globally per target and overridden per lun,
+ # so we treat it differently
+ if ($store_option ne "mode_page" && $store_option ne "write-cache") {
+ my $exec_command = add_params($store_option, $result, $lun, $driver);
+ push(@exec_commands, $exec_command);
+ $params_added{$store_option} = 1;
+ }
+ if ($store_option eq "write-cache") {
+ my $exec_command = add_params($store_option, $result, $lun, $driver);
+ $params_added{write_cache} = 1;
+ push(@exec_commands, $exec_command);
+ }
+ if ($store_option eq "device-type") {
+ $device_type = $result;
+ $params_added{$store_option} = 1;
+ }
+ if ($store_option eq "mode_page") {
+ @mode_page = @$result;
+ foreach my $mode_page (@mode_page) {
+ my $exec_command = add_params("mode_page", $mode_page, $lun, $driver);
+ push(@exec_commands, $exec_command);
+ }
+ }
+ }
}
- execute("# Adding target: $target");
- execute("tgtadm --lld $driver --op new --mode target --tid $next_tid -T $target");
- foreach my $k3 (sort keys %{$conf{$k}{$k2}}) {
- $option = $k3;
- $value = $conf{$k}{$k2}{$k3};
- &check($value);
- &process_options($driver);
- # If there was no option called "initiator-address", it means
- # we want to allow ALL initiators for this target
- if ( $option eq "initiator-address" ) {
- $allowall = 0;
+ }
+ # Used only if lun is a direct-store
+ my $sg_inq;
+ my %direct_params;
+ if ($direct_store == 1) {
+ $sg_inq=`sg_inq $backing_store`;
+ if ($sg_inq=~m {
+ Vendor\ identification:\s+?(.*?)\n
+ \s+Product\ identification:\s+(.*?)\n
+ \s+Product\ revision\ level:\s+(.*?)\n
+ (?:\s+Unit\ serial\ number:\s+(.*?)\n)?
+ }xs ) {
+ # If they were not defined globally for a target,
+ # add them now
+ if (not length $$target_options_ref{vendor_id}) {
+ $direct_params{vendor_id} = $1;
+ }
+ if (not length $$target_options_ref{product_id}) {
+ $direct_params{product_id} = $2;
+ }
+ if (not length $$target_options_ref{product_rev}) {
+ $direct_params{product_rev} = $3;
+ }
+ if (not length $$target_options_ref{scsi_sn}) {
+ $direct_params{scsi_sn} = $4;
}
}
+ }
- if ( $allowall == 1 ) {
- execute("tgtadm --lld $driver --op bind --mode target --tid $next_tid -I ALL");
+ # Add these parameters if they were not overwritten in the config file
+ my @opts = ("scsi_id", "sense_format", "removable", "online", "path");
+ foreach my $single_opt (@opts) {
+ check_if_hash_array($$target_options_ref{$single_opt},$single_opt);
+ if ($params_added{$single_opt} ne 1 && length $$target_options_ref{$single_opt}) {
+ my $exec_command = add_params($single_opt, $$target_options_ref{$single_opt}, $lun, $driver);
+ push(@exec_commands, $exec_command);
+ $params_added{$single_opt} = 1;
}
- execute();
}
+ # These options can be fetched by sg_inq for direct-store
+ my @opts = ("vendor_id", "product_id", "product_rev", "scsi_sn");
+ foreach my $single_opt (@opts) {
+ check_if_hash_array($$target_options_ref{$single_opt},$single_opt);
+ my $this_opt;
+ if (length $$target_options_ref{$single_opt}) {
+ $this_opt = $$target_options_ref{$single_opt};
+ } elsif (length $direct_params{$single_opt}) {
+ $this_opt = $direct_params{$single_opt};
+ }
+ if ($params_added{$single_opt} ne 1 && length $this_opt) {
+ my $exec_command = add_params($single_opt, $this_opt, $lun, $driver);
+ push(@exec_commands, $exec_command);
+ $params_added{$single_opt} = 1;
+ }
+ }
+ # write-cache
+ if ($params_added{write_cache} ne 1) {
+ my $exec_command = add_params("write-cache", $$target_options_ref{"write-cache"}, $lun, $driver);
+ push(@exec_commands, $exec_command);
+ $params_added{write_cache} = 1;
+ }
+ # mode_page
+ unless (ref($$target_options_ref{mode_page}) eq 'ARRAY') {
+ $$target_options_ref{mode_page} = [ $$target_options_ref{mode_page} ];
+ }
+ foreach my $mode_page (@{$$target_options_ref{"mode_page"}}) {
+ if (length $mode_page) {
+ my $exec_command = add_params("mode_page", $mode_page, $lun, $driver);
+ push(@exec_commands, $exec_command);
+ }
+ }
+ # device-type
+ if ($params_added{"device-type"} ne 1) {
+ check_if_hash_array($$target_options_ref{"device-type"}, "device-type");
+ $device_type = $$target_options_ref{"device-type"};
+ }
+ } else {
+ print "If you got here, this means your config file is not supported.\n";
+ print "Please report it to stgt mailing list and attach your config files.\n";
+ exit 1;
+ }
+ # Execute commands for a given LUN
+ if (length $device_type) { $device_type = "--device-type $device_type" };
+ execute("tgtadm --lld $driver --op new --mode logicalunit --tid $next_tid --lun $lun -b $backing_store $device_type");
+ foreach my $exec_command (@exec_commands) {
+ if (length $exec_command) { execute($exec_command) }
}
+ $lun += 1;
+ return $lun;
+ } elsif ($can_alloc == 0) {
+ execute("# Skipping device $backing_store ($dev is mounted / in use)");
+ } else {
+ execute("# Skipping device: $backing_store");
+ execute("# $backing_store does not exist - please check the configuration file");
}
}
# Process options from the config file
sub process_options {
- my $driver = $_[0];
- if ( $option eq "backing-store" ) {
- # if we have one command, force it to be an array anyway
- unless (ref($value) eq 'ARRAY') {
- $value = [ $value ];
+ my $target_options_ref = $_[0];
+ my $driver = $$target_options_ref{"driver"};
+ if ($option eq "backing-store" || $option eq "direct-store") {
+ my $direct_store = 0;
+ if ($option eq "direct-store") {
+ check_exe("sg_inq", "direct-store");
+ $direct_store = 1;
}
- my @value_arr = @$value;
- my $i = 1;
- foreach my $backing_store (@value_arr) {
- # Check if device exists
- if ( -e $backing_store) {
- execute("tgtadm --lld $driver --op new --mode logicalunit --tid $next_tid --lun $i -b $backing_store");
- $i += 1;
- }
- else {
- print("skipping device $backing_store\n");
- print("$backing_store does not exist - please check the configuration file\n");
- }
+ # We want to make everything a hash to use it
+ # in the same way later on
+ unless (ref($value)) {
+ $value = { $value }
}
- }
- if ( $option eq "direct-store" ) {
- my $inq;
- my $vendor_id="";
- my $prod_id="";
- my $prod_rev="";
- my $scsi_serial="";
- # if we have one command, force it to be an array anyway
- unless (ref($value) eq 'ARRAY') {
- $value = [ $value ];
- }
- my @value_arr = @$value;
- my $i = 1;
- foreach my $direct_store (@value_arr) {
- $inq=`sg_inq $direct_store`;
- if ($inq=~/Vendor identification:\s*(\w+)\s*\n*Product identification:\s*([\w\s\/\-]+)\n\s*\n*Product revision level:\s*(\w*)\s*\n*Unit serial number:\s*(\w+)/)
- {
- $vendor_id="$1";
- $prod_id="$2";
- $prod_rev="$3";
- $scsi_serial="$4";
+ my %arrvalue;
+ if (ref($value) eq "ARRAY") {
+ foreach my $backing_store (@$value) {
+ $arrvalue{$backing_store} = 1;
}
- $vendor_id =~ s/\s+$//;
- $prod_id =~ s/\s+$//;
- $prod_rev =~ s/\s+$//;
- $scsi_serial =~ s/\s+$//;
+ $value = \%arrvalue;
+ }
- execute("tgtadm --lld $driver --op new --mode logicalunit --tid $next_tid --lun 1 -b $direct_store");
- execute("tgtadm --lld $driver --op update --mode logicalunit --tid $next_tid --lun 1 --params vendor_id=\"$vendor_id\",product_id=\"$prod_id\",product_rev=\"$prod_rev\",scsi_sn=\"$scsi_serial\"");
- $i += 1;
+ if (ref($value) eq "HASH") {
+ foreach my $backing_store (sort keys %$value) {
+ $lun = add_backing_direct($backing_store,$target_options_ref,$lun,$direct_store);
+ }
}
}
if ( $option eq "incominguser" ) {
# if we have one command, force it to be an array anyway
- unless (ref($value) eq 'ARRAY') {
- $value = [ $value ];
- }
+ force_array();
my @value_arr = @$value;
foreach my $incominguser (@value_arr) {
my @userpass = split(/ /, $incominguser);
- &check($userpass[1]);
+ check_value($userpass[1]);
execute("tgtadm --lld $driver --mode account --op delete --user=$userpass[0]");
execute("tgtadm --lld $driver --mode account --op new --user=$userpass[0] --password=$userpass[1]");
execute("tgtadm --lld $driver --mode account --op bind --tid=$next_tid --user=$userpass[0]");
@@ -298,12 +515,10 @@ sub process_options {
if ( $option eq "outgoinguser" ) {
# if we have one command, force it to be an array anyway
- unless (ref($value) eq 'ARRAY') {
- $value = [ $value ];
- }
+ force_array();
execute("# Warning: only one outgoinguser is allowed. Will only use the first one.");
my @userpass = split(/ /, @$value[0]);
- &check($userpass[1]);
+ check_value($userpass[1]);
execute("tgtadm --lld $driver --mode account --op delete --user=$userpass[0]");
execute("tgtadm --lld $driver --mode account --op new --user=$userpass[0] --password=$userpass[1]");
execute("tgtadm --lld $driver --mode account --op bind --tid=$next_tid --user=$userpass[0] --outgoing");
@@ -311,21 +526,20 @@ sub process_options {
if ( $option eq "initiator-address" ) {
# if we have one command, force it to be an array anyway
- unless (ref($value) eq 'ARRAY') {
- $value = [ $value ];
- }
+ force_array();
my @value_arr = @$value;
foreach my $initiator_address (@value_arr) {
+ check_value($initiator_address);
execute("tgtadm --lld $driver --op bind --mode target --tid $next_tid -I $initiator_address");
}
}
}
# If the target is configured, but not present in the config file,
-# offline it and try to remove it
+# try to remove it
sub remove_targets {
- &process_targets;
+ process_targets;
my @all_targets = keys %tgtadm_output_tid;
foreach my $existing_target (@all_targets) {
@@ -340,13 +554,8 @@ sub remove_targets {
}
if ( $dontremove == 0 ) {
- # Right now, it is not possible to remove a target if any initiators
- # are connected to it. We'll do our best - offline the target first
- # (so it won't accept any new connections), and remove.
- # Note that remove will only work if no initiator is connected.
- execute("# Removing target: $existing_target");
- execute("tgtadm --op update --mode target --tid=$tgtadm_output_tid{$existing_target} -n state -v offline");
- execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$existing_target}");
+ # Remove the target
+ main_delete($existing_target);
}
}
}
@@ -356,61 +565,54 @@ sub remove_targets {
# Dump current tgtd configuration
sub dump_config {
- &process_targets;
+ process_targets;
my @all_targets = keys %tgtadm_output_tid;
- foreach my $target (@all_targets) {
- foreach my $show_target_line ($tgtadm_output{$target}) {
- if ( $show_target_line =~ m/^Target (\d*): (.+)/ ) {
- print "<target $2>\n";
- }
+ # If all targets use the same driver, us it only once in the config
+ my $skip_driver = 0;
+ my @drivers_combined;
+ foreach my $current_target (@all_targets) {
+ my $driver = show_target_info($current_target, "driver");
+ push (@drivers_combined, $driver);
+ }
- if ( $show_target_line =~ m/\s+Driver: (.+)/ ) {
- print "\tdriver $1\n";
- }
+ my %drivers_uniq;
+ @drivers_uniq{@drivers_combined} = ();
+ my @drivers_combined_uniq = sort keys %drivers_uniq;
- if ( $show_target_line =~ m/\s+Backing store: (?!No backing store)(.+)/ ) {
- print "\tbacking-store $1\n";
- }
- }
+ if (scalar @drivers_combined_uniq == 1) {
+ print "default-driver $drivers_combined_uniq[0]\n\n";
+ }
- # Process account and ACL information
- my $account_acl;
+ # Print everything else in the config
+ foreach my $current_target (@all_targets) {
+ my $target_name = show_target_info($current_target, "target_name");
+ print "<target $target_name>\n";
- foreach my $show_target_line ($tgtadm_output{$target}) {
- $account_acl .= $show_target_line
+ if (scalar @drivers_combined_uniq gt 1) {
+ my $driver = show_target_info($current_target, "driver");
+ print "\tdriver $driver\n";
}
- # start with account information...
- while ($account_acl =~ m{
- \s+Account\ information:\n(.*)ACL\ information:
- }xmgs
- ) {
-
- my @account = split(/\n/, $1);
-
- foreach my $user (@account) {
- my @var = split(/^\s+/, $user);
- @var = split(/\s/, $var[1]);
+ my @backing_stores = show_target_info($current_target, "backing_stores");
+ foreach my $backing_store (@backing_stores) {
+ print "\tbacking-store $backing_store\n";
+ }
- if ( $var[1] eq "(outgoing)" ) {
- print "\toutgoinguser $var[0] PLEASE_CORRECT_THE_PASSWORD\n";
- } elsif ( ($var[0] ne "") && ($var[1] eq "") ) {
- print "\tincominguser $var[0] PLEASE_CORRECT_THE_PASSWORD\n";
- }
+ my @account_information = show_target_info($current_target, "account_information");
+ foreach my $account (@account_information) {
+ if ($account =~ /(.+)\ \(outgoing\)/) {
+ print "\toutgoinguser $1 PLEASE_CORRECT_THE_PASSWORD\n";
+ } elsif (length $account) {
+ print "\tincominguser $account PLEASE_CORRECT_THE_PASSWORD\n";
}
}
- #...and finish with ACL information
- while ($account_acl =~ m{
- \s+ACL\ information:\n(.*)
- }xmgs
- ) {
- my @ini_addresses = split(/\n/, $1);
- foreach my $ini_address (@ini_addresses) {
- my @var = split(/^\s+/, $ini_address);
- print "\tinitiator-address $var[1]\n";
+ my @acl_information = show_target_info($current_target, "acl_information");
+ if (scalar(@acl_information) != 1 || $acl_information[0] ne "ALL") {
+ foreach my $ini_address (@acl_information) {
+ print "\tinitiator-address $ini_address\n";
}
}
print "</target>\n\n";
@@ -441,17 +643,17 @@ Example usage:
EOF
} elsif ($off_ready eq "ALL") {
- &process_targets;
+ process_targets;
# Run over all targets and offline/ready them
my @all_targets = keys %tgtadm_output_tid;
foreach my $existing_target (@all_targets) {
execute("tgtadm --op update --mode target --tid=$tgtadm_output_tid{$existing_target} -n state -v $var");
}
} elsif ($off_ready =~ m/tid=(.+)/) {
- &process_targets;
+ process_targets;
execute("tgtadm --op update --mode target --tid=$1 -n state -v $var");
} else {
- &process_targets;
+ process_targets;
if (length $tgtadm_output_tid{$off_ready}) {
execute("tgtadm --op update --mode target --tid=$tgtadm_output_tid{$off_ready} --name=\"$off_ready\" -n state -v $var");
} else {
@@ -465,17 +667,42 @@ EOF
sub show_target_info {
my $existing_target = $_[0];
my $task = $_[1];
+ # Returns target information
+ if ($task eq "target_name") {
+ if ($tgtadm_output{$existing_target} =~ m/^Target (\d*): (.+)/ ) {
+ return $2;
+ }
# Returns driver information
- if ($task eq "driver") {
- if ( $tgtadm_output{$existing_target} =~ m/\s+Driver: (.+)/ ) {
- print $1;
+ } elsif ($task eq "driver") {
+ if ($tgtadm_output{$existing_target} =~ m/\s+Driver: (.+)/ ) {
return $1;
}
+ # Returns backing store
+ } elsif ($task eq "backing_stores") {
+ if ($tgtadm_output{$existing_target} =~ m/\s+Backing store: (?!No backing store)(.+)/ ) {
+ my @backing_stores = $tgtadm_output{$existing_target} =~ m{\s+Backing store: (?!No backing store\n)(.+)}g;
+ return @backing_stores;
+ }
+ return;
+ # Returns account information:
+ } elsif ($task eq "account_information") {
+ if ($tgtadm_output{$existing_target} =~ m{
+ \s+Account\ information:\n(.*)\n\s+ACL\ information:
+ }xs
+ ) {
+ my @accounts = split(/\n/, $1);
+ my @account_information;
+ foreach my $user (@accounts) {
+ my @var = split(/^\s+/, $user);
+ push(@account_information, $var[1]);
+ }
+ return @account_information;
+ }
# Returns ACL information
} elsif ($task eq "acl_information") {
- while ($tgtadm_output{$existing_target} =~ m{
+ if ($tgtadm_output{$existing_target} =~ m{
\s+ACL\ information:\n(.*)
- }xmgs
+ }xs
) {
my @ini_addresses = split(/\n/, $1);
my @acls;
@@ -487,51 +714,80 @@ sub show_target_info {
}
# Returns sessions
} elsif ($task eq "sessions") {
- my @var = split(/\n/, $tgtadm_output{$existing_target});
- my @sids;
- foreach my $sid (@var) {
- if ( $sid =~ m/\s+I_T nexus: (.+)/ ) {
- push(@sids, $1);
+ my %sessions;
+ if ($tgtadm_output{$existing_target} =~ m{
+ \s+I_T\ nexus\ information:\n(.*)LUN\ information:
+ }xs
+ ) {
+ my @var = split(/\n/, $1);
+ my $sid;
+ my $cid;
+
+ foreach my $line (@var) {
+ if ($line =~ m/\s+I_T nexus:\ (.+)/) {
+ $sid = $1;
+ } else {
+ if ($line =~ m/\s+Connection:\ (.+)/) {
+ $cid = $1;
+ $sessions{$sid} = $cid;
+ }
+ }
}
}
- return @sids;
+ return %sessions;
}
}
-# Delete the targets which are not in use
-sub delete_targets {
-
- # Check if the target is used by an initiator
- sub check_in_use {
- my $existing_target = $_[0];
- my $cur_option = $_[1];
- my $cur_tid = $_[2];
- if ($tgtadm_output{$existing_target} =~ m/\s+Connection:/) {
- if ($force == 1) {
- # Remove ACLs first
- my @acl_info = &show_target_info($existing_target, "acl_information");
- foreach my $acl (@acl_info) {
- execute("tgtadm --op unbind --mode target --tid $tgtadm_output_tid{$existing_target} -I $acl");
- }
- # Now, remove all sessions / connections from that tid
- my @sessions = &show_target_info($existing_target, "sessions");
- foreach my $session (@sessions) {
- execute("tgtadm --op delete --mode conn --tid $tgtadm_output_tid{$existing_target} --sid $session --cid 0");
+# Main subroutine for deleting targets
+sub main_delete {
+ my $current_target = $_[0];
+ my $current_tid = $_[1];
+ my $configured = check_configured($current_target);
+ my $del_upd_text;
+ # Check if the target has initiators connected
+ if ($tgtadm_output{$current_target} =~ m/\s+Connection:/) {
+ if ($force == 1) {
+ execute("# Removing target: $current_target");
+ # Remove ACLs first
+ my @acl_info = show_target_info($current_target, "acl_information");
+ foreach my $acl (@acl_info) {
+ execute("tgtadm --op unbind --mode target --tid $tgtadm_output_tid{$current_target} -I $acl");
+ }
+ # Now, remove all sessions / connections from that tid
+ my %sessions = show_target_info($current_target, "sessions");
+ foreach my $sid (keys %sessions) {
+ foreach my $cid ($sessions{$sid}) {
+ execute("tgtadm --op delete --mode conn --tid $tgtadm_output_tid{$current_target} --sid $sid --cid $cid");
}
- execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$existing_target}");
- } else {
- execute("# Target with tid $tgtadm_output_tid{$existing_target} ($existing_target) is in use, it won't be deleted.");
}
- } elsif (length $tgtadm_output_tid{$existing_target}) {
- execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$existing_target}");
+ execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$current_target}");
} else {
- if ($cur_option eq "tid") {
- execute("# Target with tid $cur_tid does not exist!");
+ if ($update ne 0) {
+ $del_upd_text = "updated";
} else {
- execute("# Target $existing_target does not exist!");
+ $del_upd_text = "deleted";
}
+ execute("# Target with tid $tgtadm_output_tid{$current_target} ($current_target) is in use, it won't be $del_upd_text.");
+ return 1;
+ }
+ } elsif (length $tgtadm_output_tid{$current_target}) {
+ execute("# Removing target: $current_target");
+ execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$current_target}");
+ } else {
+ if (length $current_tid) {
+ execute("# Target with tid $current_tid does not exist!");
+ } else {
+ execute("# Target with name $current_target does not exist!");
}
}
+ if ($configured ne 0) {
+ execute();
+ }
+ return 0;
+}
+
+# Delete the targets
+sub delete_targets {
if ($delete eq "help") {
print <<EOF;
@@ -539,7 +795,7 @@ sub delete_targets {
The target will be deleted only if it's not used
(no initiator is connected to it).
If you want to delete targets which are in use,
- you have to add "--force" flag
+ you have to add "--force" flag.
Example usage:
--delete help - display this help
@@ -550,27 +806,131 @@ Example usage:
EOF
exit;
} elsif ($delete eq "ALL") {
- &process_targets;
- # Run over all targets and delete them if they are not in use
+ process_targets;
+ # Run over all targets and delete them
my @all_targets = keys %tgtadm_output_tid;
- foreach my $existing_target (@all_targets) {
- &check_in_use($existing_target);
+ foreach my $current_target (@all_targets) {
+ main_delete($current_target);
}
- } elsif ($delete =~ m/tid=(.+)/) {
+ } elsif ($delete =~ m/^tid=(.+)/) {
# Delete by tid
- &process_targets;
- my $existing_target = $1;
- &check_in_use($tgtadm_output_name{$existing_target}, "tid", $existing_target);
+ process_targets;
+ my $current_target = $tgtadm_output_name{$1};
+ main_delete($current_target, $1);
} else {
# Delete by name
- &process_targets;
- my $existing_target = $delete;
- &check_in_use($existing_target);
+ process_targets;
+ my $current_target = $delete;
+ main_delete($current_target);
}
}
-# Some checks
-sub check {
+# Update targets
+sub update_targets {
+ if ($update eq "help") {
+ print <<EOF;
+ --update <value> update all or selected targets
+ The target will be updated only if it's not used
+ (no initiator is connected to it).
+ If you want to update targets which are in use,
+ you have to add "--force" flag.
+
+Example usage:
+ --update help - display this help
+ --update ALL - update all targets
+ --update tid=4 - update target 4 (target with tid 4)
+ --update iqn.2008-08.com.example:some.target - update this target
+
+EOF
+ exit;
+ } elsif ($update eq "ALL") {
+ # Run over all targets and delete them if they are not in use
+ parse_configs;
+ process_targets;
+ my @targets_combined = combine_targets();
+ foreach my $current_target (@targets_combined) {
+ my $configured = check_configured($current_target);
+ my $connected = check_connected($current_target);
+ my $in_configfile = check_in_configfile($current_target);
+ combine_targets();
+ if (($in_configfile == 0) && ($configured == 1)) {
+ # Delete the target if it's not in the config file
+ main_delete($current_target);
+ } else {
+ add_targets($current_target, $configured, $connected, $in_configfile);
+ }
+
+ }
+ } elsif ($update =~ m/^tid=(.+)/) {
+ # Update by tid
+ parse_configs;
+ process_targets;
+ my $current_target = $tgtadm_output_name{$1};
+ my $configured = check_configured($current_target);
+ my $connected = check_connected($current_target);
+ my $in_configfile = check_in_configfile($current_target);
+ if (($in_configfile == 0) && ($configured == 1)) {
+ # Delete the target if it's not in the config file
+ main_delete($current_target);
+ } elsif ($configured == 1) {
+ add_targets($current_target, $configured, $connected, $in_configfile);
+ } else {
+ print "There is no target with tid $1, can't continue!\n";
+ exit 1;
+ }
+ } else {
+ # Update by name
+ parse_configs;
+ process_targets;
+ my $current_target = $update;
+ my $configured = check_configured($current_target);
+ my $connected = check_connected($current_target);
+ my $in_configfile = check_in_configfile($current_target);
+ if ($in_configfile == 0 && $configured == 1) {
+ # Delete the target if it's not in the config file
+ main_delete($current_target);
+ } else {
+ add_targets($current_target, $configured, $connected, $in_configfile);
+ }
+ }
+}
+
+# Find the biggest tid
+sub find_max_tid {
+ process_targets;
+ my @all_targets = keys %tgtadm_output_tid;
+ my $maxtid = 0;
+ foreach my $var (@all_targets) {
+ if ($tgtadm_output_tid{$var} > $maxtid) {
+ $maxtid = $tgtadm_output_tid{$var};
+ }
+ }
+ return $maxtid;
+}
+
+# Combine targets from the config file and currently configured targets
+sub combine_targets {
+ my @targets_in_configfile;
+ my @all_targets = keys %tgtadm_output_tid;
+ my @targets_combined;
+ # Make an array of targets in the config file
+ foreach my $k (sort keys %conf) {
+ if ( $k eq "target" ) {
+ foreach my $k2 (sort keys %{$conf{$k}}) {
+ push(@targets_in_configfile, $k2)
+ }
+ }
+ }
+ # Use only unique elements from both arrays
+ foreach my $current_target (@all_targets) {
+ push (@targets_combined, $current_target) unless grep { $_ eq $current_target } @targets_in_configfile;
+ }
+ @targets_combined = (@targets_combined, @targets_in_configfile);
+ return @targets_combined;
+}
+
+# Check if a value is correct
+sub check_value {
if ( not defined $_[0] or not length $_[0] ) {
print "\nOption $option has a missing value!\n";
print "Check your config file for errors (target: $target)\n";
@@ -578,6 +938,102 @@ sub check {
}
}
+# Check if the target is in the config file
+sub check_in_configfile {
+ my $current_target = $_[0];
+ my $result;
+ foreach my $k (sort keys %conf) {
+ if ( $k eq "target" ) {
+ foreach my $k2 (sort keys %{$conf{$k}}) {
+ if ($k2 eq $current_target) {
+ return 1;
+ }
+ }
+ # If we're here, we didn't find a match
+ return 0;
+ }
+ }
+}
+
+# Check if the target is configured in tgtd
+sub check_configured {
+ my $current_target = $_[0];
+ if (length $tgtadm_output_tid{$current_target}) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+# Check if any initiators are connected to the target
+sub check_connected {
+ my $current_target = $_[0];
+ if ($tgtadm_output{$current_target} =~ m/\s+Connection:/) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+# Check if a device can be allocated
+my @rootfs_dev;
+sub check_device {
+ my $tmp_dev = $_[0];
+
+ # Check if force flag is set
+ if ( $force == 0) {
+ # Check for rootfs devices
+ &find_rootfs_device();
+ $tmp_dev =~ s/\d//g;
+ # Check if device is on the same disk as rootfs
+ if (grep {$_ eq $tmp_dev} @rootfs_dev) {
+ return (0,$tmp_dev);
+ }
+ }
+ return 1;
+}
+
+# finds all the devices that rootfs is mounted on
+sub find_rootfs_device {
+ my @files=("/etc/mtab","/proc/mounts");
+ my @lines;
+ # read files
+ foreach my $file (@files){
+ if (open(FH,"$file")) {
+ @lines=(@lines,<FH>);
+ close (FH);
+ }
+ }
+
+ # parse files and finds all the device which mounted on /
+ foreach my $line (@lines){
+ chomp $line;
+ if (($line=~/^\/dev\//) && ($line=~/ \/ /)){
+ my @ln=split(' ',$line);
+ $ln[0]=~s/\d//g;
+ push(@rootfs_dev,$ln[0]);
+ }
+ }
+
+ # read swap file
+ my $swap_file="/proc/swap";
+ if (open(FH,"$swap_file")) {
+ @lines=<FH>;
+ close (FH);
+ }
+ # parse swap file and finds all the swap devices
+ foreach my $line (@lines){
+ chomp $line;
+ if ($line=~/^\/dev\//) {
+ my @ln=split(' ',$line);
+ $ln[0]=~s/\d//g;
+ push(@rootfs_dev,$ln[0]);
+ }
+ }
+ # remove duplicate entries from @rootfs_dev
+ my %seen = ();
+ @rootfs_dev = grep { ! $seen{ $_ }++ } @rootfs_dev;
+}
# Execute or just print (or both) everything we start or would start
sub execute {
@@ -589,7 +1045,7 @@ sub execute {
}
# Don't try to execute if it's a comment
my @execargs = split(/#/, $args);
- if ( $execargs[0] ne undef ) {
+ if ($execargs[0] ne undef) {
system($args);
# If non-zero exit code was return, exit
@@ -600,24 +1056,26 @@ sub execute {
}
}
- } elsif ( $pretend == 1 ) {
+ } elsif ($pretend == 1) {
print "@_\n";
}
}
if ($execute == 1) {
- &process_targets;
- &parse_configs;
- &add_targets;
- &remove_targets;
+ process_targets;
+ parse_configs;
+ add_targets;
+ remove_targets;
} elsif ($delete ne 0) {
- &delete_targets;
+ delete_targets;
+} elsif ($update ne 0) {
+ update_targets;
} elsif ($dump == 1) {
- &dump_config;
+ dump_config;
} elsif ($offline ne 0) {
- &ready_offline_targets("offline");
+ ready_offline_targets("offline");
} elsif ($ready ne 0) {
- &ready_offline_targets("ready");
+ ready_offline_targets("ready");
} else {
print "No action specified.\n";
}
diff --git a/usr/Makefile b/usr/Makefile
index 4245709..82ddf07 100644
--- a/usr/Makefile
+++ b/usr/Makefile
@@ -55,10 +55,12 @@ CFLAGS += -g -O2 -Wall -Wstrict-prototypes -fPIC
LIBS += -lpthread
PROGRAMS += tgtd tgtadm
-SCRIPTS += ../scripts/tgt-setup-lun
+SCRIPTS += ../scripts/tgt-setup-lun ../scripts/tgt-admin
TGTD_OBJS += tgtd.o mgmt.o target.o scsi.o log.o driver.o util.o work.o \
- parser.o spc.o sbc.o mmc.o osd.o scc.o smc.o ssc.o bs_ssc.o bs.o
-MANPAGES = ../doc/manpages/tgtadm.8 ../doc/manpages/tgt-setup-lun.8
+ parser.o spc.o sbc.o mmc.o osd.o scc.o smc.o ssc.o bs_ssc.o \
+ bs.o
+MANPAGES = ../doc/manpages/tgtadm.8 ../doc/manpages/tgt-admin.8 \
+ ../doc/manpages/tgt-setup-lun.8
TGTD_DEP = $(TGTD_OBJS:.o=.d)
diff --git a/usr/be_byteshift.h b/usr/be_byteshift.h
new file mode 100644
index 0000000..5c6a619
--- /dev/null
+++ b/usr/be_byteshift.h
@@ -0,0 +1,68 @@
+#ifndef _LINUX_UNALIGNED_BE_BYTESHIFT_H
+#define _LINUX_UNALIGNED_BE_BYTESHIFT_H
+
+static inline uint16_t __get_unaligned_be16(const uint8_t *p)
+{
+ return p[0] << 8 | p[1];
+}
+
+static inline uint32_t __get_unaligned_be32(const uint8_t *p)
+{
+ return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+}
+
+static inline uint64_t __get_unaligned_be64(const uint8_t *p)
+{
+ return (uint64_t)__get_unaligned_be32(p) << 32 |
+ __get_unaligned_be32(p + 4);
+}
+
+static inline void __put_unaligned_be16(uint16_t val, uint8_t *p)
+{
+ *p++ = val >> 8;
+ *p++ = val;
+}
+
+static inline void __put_unaligned_be32(uint32_t val, uint8_t *p)
+{
+ __put_unaligned_be16(val >> 16, p);
+ __put_unaligned_be16(val, p + 2);
+}
+
+static inline void __put_unaligned_be64(uint64_t val, uint8_t *p)
+{
+ __put_unaligned_be32(val >> 32, p);
+ __put_unaligned_be32(val, p + 4);
+}
+
+static inline uint16_t get_unaligned_be16(const void *p)
+{
+ return __get_unaligned_be16((const uint8_t *)p);
+}
+
+static inline uint32_t get_unaligned_be32(const void *p)
+{
+ return __get_unaligned_be32((const uint8_t *)p);
+}
+
+static inline uint64_t get_unaligned_be64(const void *p)
+{
+ return __get_unaligned_be64((const uint8_t *)p);
+}
+
+static inline void put_unaligned_be16(uint16_t val, void *p)
+{
+ __put_unaligned_be16(val, p);
+}
+
+static inline void put_unaligned_be32(uint32_t val, void *p)
+{
+ __put_unaligned_be32(val, p);
+}
+
+static inline void put_unaligned_be64(uint64_t val, void *p)
+{
+ __put_unaligned_be64(val, p);
+}
+
+#endif /* _LINUX_UNALIGNED_BE_BYTESHIFT_H */
diff --git a/usr/bs.c b/usr/bs.c
index f100e2c..cef7b19 100644
--- a/usr/bs.c
+++ b/usr/bs.c
@@ -125,7 +125,15 @@ static void bs_thread_request_done(int fd, int events, void *data)
cmd->scsi_cmd_done(cmd, scsi_get_result(cmd));
}
- write(info->command_fd[1], &nr_events, sizeof(nr_events));
+rewrite:
+ ret = write(info->command_fd[1], &nr_events, sizeof(nr_events));
+ if (ret < 0) {
+ eprintf("can't write done, %m\n");
+ if (errno == EAGAIN || errno == EINTR)
+ goto rewrite;
+
+ return;
+ }
}
static void *bs_thread_worker_fn(void *arg)
@@ -140,7 +148,7 @@ static void *bs_thread_worker_fn(void *arg)
pthread_cond_wait(&info->pending_cond, &info->pending_lock);
if (info->stop) {
pthread_mutex_unlock(&info->pending_lock);
- break;
+ pthread_exit(NULL);
}
goto retest;
}
@@ -200,11 +208,32 @@ int bs_thread_open(struct bs_thread_info *info, request_func_t *rfn)
for (i = 0; i < ARRAY_SIZE(info->worker_thread); i++) {
ret = pthread_create(&info->worker_thread[i], NULL,
bs_thread_worker_fn, info);
+ if (ret)
+ goto destroy_threads;
+ }
+rewrite:
+ ret = write(info->command_fd[1], &ret, sizeof(ret));
+ if (ret < 0) {
+ eprintf("can't write done, %m\n");
+ if (errno == EAGAIN || errno == EINTR)
+ goto rewrite;
}
+ return 0;
+destroy_threads:
write(info->command_fd[1], &ret, sizeof(ret));
+ pthread_cancel(info->ack_thread);
+ pthread_cond_signal(&info->finished_cond);
+ pthread_join(info->ack_thread, NULL);
- return 0;
+ info->stop = 1;
+ for (i = 0; info->worker_thread[i]; i++) {
+ pthread_cancel(info->worker_thread[i]);
+ pthread_cond_signal(&info->pending_cond);
+ }
+
+ for (i = 0; info->worker_thread[i]; i++)
+ pthread_join(info->worker_thread[i], NULL);
event_del:
tgt_event_del(info->done_fd[0]);
close_done_fd:
diff --git a/usr/bs_aio.h b/usr/bs_aio.h
index ad1cc3a..f62e99c 100644
--- a/usr/bs_aio.h
+++ b/usr/bs_aio.h
@@ -44,6 +44,10 @@ enum {
#define __NR_eventfd 284
#elif defined(__i386__)
#define __NR_eventfd 323
+#elif defined(__powerpc__)
+#define __NR_eventfd 307
+#elif defined(__powerpc64__)
+#define __NR_eventfd 307
#elif defined(__ia64__)
#define __NR_eventfd 1309
#elif defined(__sparc__) || defined(__sparc64__)
diff --git a/usr/log.c b/usr/log.c
index 4b71216..076c770 100644
--- a/usr/log.c
+++ b/usr/log.c
@@ -108,9 +108,6 @@ static int logarea_init (int size)
return 1;
}
- la->ops[0].sem_num = 0;
- la->ops[0].sem_flg = 0;
-
return 0;
}
@@ -237,21 +234,24 @@ static void log_syslog (void * buff)
static void dolog(int prio, const char *fmt, va_list ap)
{
struct timespec ts;
+ struct sembuf ops;
if (la) {
ts.tv_sec = 0;
ts.tv_nsec = 10000;
- la->ops[0].sem_op = -1;
- if (semtimedop(la->semid, la->ops, 1, &ts) < 0) {
+ ops.sem_num = 0;
+ ops.sem_flg = 0;
+ ops.sem_op = -1;
+ if (semtimedop(la->semid, &ops, 1, &ts) < 0) {
syslog(LOG_ERR, "semop up failed");
return;
}
log_enqueue(prio, fmt, ap);
- la->ops[0].sem_op = 1;
- if (semop(la->semid, la->ops, 1) < 0) {
+ ops.sem_op = 1;
+ if (semop(la->semid, &ops, 1) < 0) {
syslog(LOG_ERR, "semop down failed");
return;
}
@@ -291,15 +291,21 @@ void log_debug(const char *fmt, ...)
static void log_flush(void)
{
+ struct sembuf ops;
+
while (!la->empty) {
- la->ops[0].sem_op = -1;
- if (semop(la->semid, la->ops, 1) < 0) {
+ ops.sem_num = 0;
+ ops.sem_flg = 0;
+ ops.sem_op = -1;
+ if (semop(la->semid, &ops, 1) < 0) {
syslog(LOG_ERR, "semop up failed");
exit(1);
}
+
log_dequeue(la->buff);
- la->ops[0].sem_op = 1;
- if (semop(la->semid, la->ops, 1) < 0) {
+
+ ops.sem_op = 1;
+ if (semop(la->semid, &ops, 1) < 0) {
syslog(LOG_ERR, "semop down failed");
exit(1);
}
diff --git a/usr/log.h b/usr/log.h
index 6993235..b84f6d6 100644
--- a/usr/log.h
+++ b/usr/log.h
@@ -55,7 +55,6 @@ struct logarea {
void *start;
void *end;
char *buff;
- struct sembuf ops[1];
int semid;
union semun semarg;
};
diff --git a/usr/parser.c b/usr/parser.c
index 7b892a5..0b244e4 100644
--- a/usr/parser.c
+++ b/usr/parser.c
@@ -30,6 +30,7 @@ static int match_one(char *s, char *p, substring_t args[])
{
char *meta;
int argc = 0;
+ unsigned long long ret;
if (!p)
return 1;
@@ -68,16 +69,16 @@ static int match_one(char *s, char *p, substring_t args[])
args[argc].to = s + len;
break;
case 'd':
- strtol(s, &args[argc].to, 0);
+ ret = strtol(s, &args[argc].to, 0);
goto num;
case 'u':
- strtoul(s, &args[argc].to, 0);
+ ret = strtoul(s, &args[argc].to, 0);
goto num;
case 'o':
- strtoul(s, &args[argc].to, 8);
+ ret = strtoul(s, &args[argc].to, 8);
goto num;
case 'x':
- strtoul(s, &args[argc].to, 16);
+ ret = strtoul(s, &args[argc].to, 16);
num:
if (args[argc].to == args[argc].from)
return 0;
diff --git a/usr/smc.c b/usr/smc.c
index 9d7f681..ab36e9c 100644
--- a/usr/smc.c
+++ b/usr/smc.c
@@ -225,6 +225,41 @@ static int build_element_descriptors(uint8_t *data, struct list_head *head,
}
/**
+ * smc_initialize_element_status with range
+ * - INITIALIZE ELEMENT STATUS WITH RANGE op code
+ *
+ * Support the SCSI op code INITIALIZE_ELEMENT_STATUS_WITH_RANGE
+ * Ref: smc3r11, 6.5
+ */
+static int smc_initialize_element_status_range(int host_no, struct scsi_cmd *cmd)
+{
+ scsi_set_in_resid_by_actual(cmd, 0);
+
+ if (device_reserved(cmd))
+ return SAM_STAT_RESERVATION_CONFLICT;
+ else
+ return SAM_STAT_GOOD;
+}
+
+/**
+ * smc_initialize_element_status - INITIALIZE ELEMENT STATUS op code
+ *
+ * Some backup libraries seem to require this.
+ *
+ * Support the SCSI op code INITIALIZE_ELEMENT_STATUS
+ * Ref: smc3r10a, 6.2
+ */
+static int smc_initialize_element_status(int host_no, struct scsi_cmd *cmd)
+{
+ scsi_set_in_resid_by_actual(cmd, 0);
+
+ if (device_reserved(cmd))
+ return SAM_STAT_RESERVATION_CONFLICT;
+ else
+ return SAM_STAT_GOOD;
+}
+
+/**
* smc_read_element_status - READ ELEMENT STATUS op code
*
* Support the SCSI op code READ ELEMENT STATUS
@@ -748,7 +783,7 @@ struct device_type_template smc_template = {
{spc_illegal_op,},
{spc_illegal_op,},
{spc_illegal_op,},
- {spc_illegal_op,},
+ {smc_initialize_element_status,},
{spc_illegal_op,},
{spc_illegal_op,},
@@ -778,7 +813,28 @@ struct device_type_template smc_template = {
{spc_illegal_op,},
{spc_illegal_op,},
- [0x20 ... 0x4f] = {spc_illegal_op,},
+ [0x20 ... 0x2f] = {spc_illegal_op,},
+
+ /* 0x30 */
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {smc_initialize_element_status_range,},
+
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+
+ [0x40 ... 0x4f] = {spc_illegal_op,},
/* 0x50 */
{spc_illegal_op,},
diff --git a/usr/spc.c b/usr/spc.c
index bd2c975..60fd7d7 100644
--- a/usr/spc.c
+++ b/usr/spc.c
@@ -318,6 +318,16 @@ int spc_test_unit(int host_no, struct scsi_cmd *cmd)
return SAM_STAT_CHECK_CONDITION;
}
+int spc_prevent_allow_media_removal(int host_no, struct scsi_cmd *cmd)
+{
+ /* TODO: implement properly */
+
+ if (device_reserved(cmd))
+ return SAM_STAT_RESERVATION_CONFLICT;
+ else
+ return SAM_STAT_GOOD;
+}
+
int spc_mode_select(int host_no, struct scsi_cmd *cmd,
int (*update)(struct scsi_cmd *, uint8_t *, int *))
{
diff --git a/usr/spc.h b/usr/spc.h
index 8fe3e3c..cfc9cf3 100644
--- a/usr/spc.h
+++ b/usr/spc.h
@@ -8,6 +8,7 @@ extern int spc_report_luns(int host_no, struct scsi_cmd *cmd);
extern int spc_start_stop(int host_no, struct scsi_cmd *cmd);
extern int spc_test_unit(int host_no, struct scsi_cmd *cmd);
extern int spc_request_sense(int host_no, struct scsi_cmd *cmd);
+extern int spc_prevent_allow_media_removal(int host_no, struct scsi_cmd *cmd);
extern int spc_illegal_op(int host_no, struct scsi_cmd *cmd);
extern int spc_lu_init(struct scsi_lu *lu);
diff --git a/usr/ssc.c b/usr/ssc.c
index 2630a6a..96c3242 100644
--- a/usr/ssc.c
+++ b/usr/ssc.c
@@ -192,7 +192,7 @@ static struct device_type_template ssc_template = {
{spc_start_stop,},
{spc_illegal_op,},
{spc_illegal_op,},
- {spc_illegal_op,},
+ {spc_prevent_allow_media_removal,},
{spc_illegal_op,},
/* 0x20 */
@@ -298,7 +298,7 @@ static struct device_type_template ssc_template = {
{spc_report_luns,},
{spc_illegal_op,},
{spc_illegal_op,},
- {spc_illegal_op,},
+ {spc_maint_in, maint_in_service_actions,},
{spc_illegal_op,},
{spc_illegal_op,},
{spc_illegal_op,},
diff --git a/usr/target.c b/usr/target.c
index 70bf72a..32812d9 100644
--- a/usr/target.c
+++ b/usr/target.c
@@ -491,7 +491,7 @@ int tgt_device_create(int tid, int dev_type, uint64_t lun, char *params,
if (lu->bst->bs_init) {
ret = lu->bst->bs_init(lu);
if (ret)
- goto fail_bs_init;
+ goto fail_lu_init;
}
if (backing && !path && !lu->attrs.removable) {
diff --git a/usr/util.h b/usr/util.h
index ac4b380..794c70b 100644
--- a/usr/util.h
+++ b/usr/util.h
@@ -6,6 +6,7 @@
#include <unistd.h>
#include <errno.h>
#include <endian.h>
+#include "be_byteshift.h"
#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))