1158 lines
35 KiB
Plaintext
1158 lines
35 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/scripts/tgt-admin b/scripts/tgt-admin
|
||
|
index fe95723..22c4725 100755
|
||
|
--- a/scripts/tgt-admin
|
||
|
+++ b/scripts/tgt-admin
|
||
|
@@ -30,6 +30,8 @@ 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
|
||
|
@@ -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,
|
||
|
@@ -147,7 +151,10 @@ my $option;
|
||
|
my $value;
|
||
|
|
||
|
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" ) {
|
||
|
@@ -170,57 +177,85 @@ sub add_targets {
|
||
|
foreach my $k (sort keys %conf) {
|
||
|
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)) {
|
||
|
+ my $maxtid = &find_max_tid;
|
||
|
+ $next_tid = $maxtid + 1;
|
||
|
+ } elsif (length $single_target) {
|
||
|
+ $next_tid = $tgtadm_output_tid{$target};
|
||
|
+ } else {
|
||
|
+ $next_tid = $next_tid + 1;
|
||
|
+ }
|
||
|
+ # 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($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;
|
||
|
+ }
|
||
|
+ $driver = $value;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (not defined $driver) {
|
||
|
+ $driver = $default_driver;
|
||
|
+ }
|
||
|
|
||
|
- # 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;
|
||
|
+ 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($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;
|
||
|
}
|
||
|
- $driver = $value;
|
||
|
}
|
||
|
- }
|
||
|
|
||
|
- if ( not defined $driver ) {
|
||
|
- $driver = $default_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);
|
||
|
- &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;
|
||
|
+ if ($allowall == 1) {
|
||
|
+ execute("tgtadm --lld $driver --op bind --mode target --tid $next_tid -I ALL");
|
||
|
}
|
||
|
- }
|
||
|
|
||
|
- 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!");
|
||
|
+ }
|
||
|
}
|
||
|
- execute();
|
||
|
}
|
||
|
+ if (length $single_target && $in_configfile == 0 && $configured == 0) {
|
||
|
+ print "Target $single_target is $connected currently not configured\n";
|
||
|
+ print "and does not exist in the config file - can't continue!\n";
|
||
|
+ exit 1;
|
||
|
+ }
|
||
|
+ execute();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
@@ -262,22 +297,29 @@ sub process_options {
|
||
|
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";
|
||
|
- }
|
||
|
- $vendor_id =~ s/\s+$//;
|
||
|
- $prod_id =~ s/\s+$//;
|
||
|
- $prod_rev =~ s/\s+$//;
|
||
|
- $scsi_serial =~ s/\s+$//;
|
||
|
+ # Check if device exists
|
||
|
+ if ( -e $direct_store) {
|
||
|
+ $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";
|
||
|
+ }
|
||
|
+ $vendor_id =~ s/\s+$//;
|
||
|
+ $prod_id =~ s/\s+$//;
|
||
|
+ $prod_rev =~ s/\s+$//;
|
||
|
+ $scsi_serial =~ s/\s+$//;
|
||
|
|
||
|
- 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;
|
||
|
+ 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;
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ print("skipping device $direct_store\n");
|
||
|
+ print("$direct_store does not exist - please check the configuration file\n");
|
||
|
+ }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@@ -289,7 +331,7 @@ sub process_options {
|
||
|
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]");
|
||
|
@@ -303,7 +345,7 @@ sub process_options {
|
||
|
}
|
||
|
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");
|
||
|
@@ -322,7 +364,7 @@ sub process_options {
|
||
|
}
|
||
|
|
||
|
# 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;
|
||
|
@@ -340,13 +382,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);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
@@ -360,57 +397,50 @@ sub dump_config {
|
||
|
|
||
|
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";
|
||
|
@@ -465,17 +495,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 +542,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 +623,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
|
||
|
@@ -551,26 +635,130 @@ EOF
|
||
|
exit;
|
||
|
} elsif ($delete eq "ALL") {
|
||
|
&process_targets;
|
||
|
- # Run over all targets and delete them if they are not in use
|
||
|
+ # 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);
|
||
|
+ 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);
|
||
|
+ 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 update 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 @all_targets = keys %tgtadm_output_tid;
|
||
|
+ 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);
|
||
|
+ } 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);
|
||
|
+ }
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+# Find the biggest tid
|
||
|
+sub find_max_tid {
|
||
|
+ 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 +766,42 @@ 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;
|
||
|
+ }
|
||
|
+}
|
||
|
|
||
|
# Execute or just print (or both) everything we start or would start
|
||
|
sub execute {
|
||
|
@@ -612,6 +836,8 @@ if ($execute == 1) {
|
||
|
&remove_targets;
|
||
|
} elsif ($delete ne 0) {
|
||
|
&delete_targets;
|
||
|
+} elsif ($update ne 0) {
|
||
|
+ &update_targets;
|
||
|
} elsif ($dump == 1) {
|
||
|
&dump_config;
|
||
|
} elsif ($offline ne 0) {
|
||
|
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/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/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/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]))
|