File tgt-0.9.0-update of Package tgt

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]))
openSUSE Build Service is sponsored by