3
# This tools parses /etc/tgt/targets.conf file and configures tgt
5
# Author: Tomasz Chmielewski
10
use Config::General qw(ParseConfig);
14
my $configfile = "/etc/tgt/targets.conf";
20
This tool configures tgt targets.
22
-e, --execute read $configfile and execute tgtadm commands
23
--delete <value> delete all or selected targets
24
(see "--delete help" for more info)
25
--offline <value> put all or selected targets in offline state
26
(see "--offline help" for more info)
27
--ready <value> put all or selected targets in ready state
28
(see "--ready help" for more info)
29
--update <value> update configuration for all or selected targets
30
(see "--update help" for more info)
31
-s, --show show all the targets
32
-c, --conf <conf file> specify an alternative configuration file
33
--ignore-errors continue even if tgtadm exits with non-zero code
34
-f, --force force some operations even if the target is in use
35
-p, --pretend only print tgtadm options
36
--dump dump current tgtd configuration (note: does not
37
include detailed parameters, like write caching)
38
-v, --verbose increase verbosity (show tgtadm commands)
39
-h, --help show this help
53
my $alternate_conf="0";
54
my $ignore_errors = 0;
60
my $result = GetOptions (
61
"e|execute" => \$execute,
62
"delete=s" => \$delete,
63
"offline=s" => \$offline,
65
"update=s" => \$update,
67
"c|conf=s" => \$alternate_conf,
68
"ignore-errors" => \$ignore_errors,
70
"p|pretend" => \$pretend,
72
"v|verbose" => \$verbose,
76
if (($help == 1) || ($param eq undef)) {
80
# Show all the targets and exit
82
execute("tgtadm --op show --mode target");
86
# Some variables/arrays/hashes we will use globally
88
my %tgtadm_output_tid;
89
my %tgtadm_output_name;
93
# Look up which targets are configured
95
# We need to run as root
97
die("You must be root to run this program.\n");
100
my @show_target = `tgtadm --op show --mode target`;
103
# Here, we create hashes of target names (all target data) and target tids
104
foreach my $show_target_line (@show_target) {
105
if ( $show_target_line =~ m/^Target (\d*): (.+)/ ) {
108
$tgtadm_output{$targetname} = $show_target_line;
109
$tgtadm_output_tid{$targetname} = $tid;
110
$tgtadm_output_name{$tid} = $targetname;
112
$tgtadm_output{$targetname} .= $show_target_line;
115
# What is the largest tid?
116
my @tids = values %tgtadm_output_tid;
117
@largest_tid = sort { $a <=> $b } @tids;
118
$next_tid = $largest_tid[$#largest_tid];
121
# Parse config file(s)
124
if ($alternate_conf ne 0) {
125
# Check if alternative configuration file exists
126
if (-e "$alternate_conf") {
127
execute("# Using $alternate_conf as configuration file\n");
128
%conf = ParseConfig(-ConfigFile => "$alternate_conf", -UseApacheInclude => 1, -IncludeGlob => 1,);
131
die("Config file $alternate_conf not found. Exiting...\n");
134
# Parse the config file with Config::General
135
if (-e "$configfile") {
136
%conf = ParseConfig(-ConfigFile => "$configfile", -UseApacheInclude => 1, -IncludeGlob => 1,);
138
die("Config file $configfile not found. Exiting...\n");
143
# Add targets, if they are not configured already
151
my $single_target = $_[0];
152
my $configured = $_[1];
153
my $connected = $_[2];
154
my $in_configfile = $_[3];
155
foreach my $k (sort keys %conf) {
157
if ($k eq "default-driver") {
158
if (not length ref($conf{$k})) {
159
$default_driver = $conf{$k};
161
print "Multiple default-driver definitions are not allowed!\n";
162
print "Check your config file for errors.\n";
165
} elsif ($k eq "ignore-errors") {
166
if ($conf{$k} eq "yes") {
172
# If $default_driver is empty, default to iscsi
173
if (not defined $default_driver) {
174
execute("# default-driver not defined, defaulting to iscsi.\n");
175
$default_driver = "iscsi";
178
foreach my $k (sort keys %conf) {
179
if ($k eq "target") {
180
foreach my $k2 (sort keys %{$conf{$k}}) {
181
# Do we run update or execute?
182
if (length $single_target) {
183
if ($single_target ne $k2) {
186
$target = $single_target;
193
if (length $single_target) {
194
$in_use = main_delete($target);
197
if ((not defined $tgtadm_output{$k2}) ||
198
($update ne 0 && $in_use == 0) ||
199
($update ne 0 && $in_use == 1 && $pretend == 1 && $force == 1))
201
# We have to find available tid
202
if ($in_configfile == 1 && $configured == 0 && $pretend == 0) {
203
my $maxtid = find_max_tid();
204
$next_tid = $maxtid + 1;
205
} elsif (length $single_target && $configured == 1) {
206
$next_tid = $tgtadm_output_tid{$target};
208
$next_tid = $next_tid + 1;
211
# Before we add a target, we need to know its type
212
# and other parameters which can be specified globally
214
my $target_options_ref;
216
foreach my $k3 (sort keys %{$conf{$k}{$k2}}) {
219
$value = $conf{$k}{$k2}{$k3};
221
$target_options{$option} = $value;
222
$target_options_ref = \%target_options;
223
$data_key = make_key($target_options_ref, "lun", "allow-in-use");
226
if (not defined $target_options{"driver"}) {
227
$target_options{"driver"} = $default_driver;
229
my $driver = $target_options{"driver"};
230
execute("# Adding target: $target");
231
execute("tgtadm --lld $driver --op new --mode target --tid $next_tid -T $target");
232
foreach my $k3 (sort keys %{$conf{$k}{$k2}}) {
234
$value = $conf{$k}{$k2}{$k3};
236
process_options($target_options_ref,$data_key);
237
# If there was no option called "initiator-address", it means
238
# we want to allow ALL initiators for this target
239
if ($option eq "initiator-address") {
244
if ($allowall == 1) {
245
execute("tgtadm --lld $driver --op bind --mode target --tid $next_tid -I ALL");
249
if (not length $configured || $in_use eq 1) {
250
execute("# Target $target already exists!");
254
if (length $single_target && $in_configfile == 0 && $configured == 0) {
255
print "Target $single_target is currently not configured\n";
256
print "and does not exist in the config file - can't continue!\n";
264
# Pre-parse the config and get some values we need
266
my $target_options_ref = shift;
270
foreach my $action (@actions) {
271
if (ref $$target_options_ref{'backing-store'} eq "HASH") {
272
foreach my $testlun (keys %{$$target_options_ref{'backing-store'}}) {
273
$data_key{$testlun}{$action} = $$target_options_ref{'backing-store'}{$testlun}{$action};
276
if (ref $$target_options_ref{'direct-store'} eq "HASH") {
277
foreach my $testlun (keys %{$$target_options_ref{'direct-store'}}) {
278
$data_key{$testlun}{$action} = $$target_options_ref{'direct-store'}{$testlun}{$action};
285
# Some options can be specified only once
286
sub check_if_hash_array {
288
my $definition = $_[1];
289
if (ref($check) eq 'ARRAY' || ref($check) eq "HASH") {
290
print "Multiple '$definition' definitions in '$option' not allowed!\n";
291
print "Check your config file for errors (target: $target).\n";
296
# Force an array if we just have one command
298
unless (ref($value) eq 'ARRAY') {
303
# If we start any external command, we want to know if it exists
307
my @path = split(":", $ENV{PATH});
309
foreach my $path (@path) {
310
if ( -x "$path/$command" && -f "$path/$command" ) { $exists = 1 }
313
if ($command eq "sg_inq") {
314
print "Command '$command' (needed by '$option') is not in your path - can't continue!\n";
316
} elsif ($command eq "lsof") {
317
execute("# Command '$command' is not in your path.");
318
execute("# Can't reliably check if device is not in use.");
324
# Apply additional parameters
327
my $param_value = shift;
331
if ($param eq "write-cache") {
332
if ($param_value eq "off") {
333
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");
334
} elsif ($param_value eq "on" || not length $param_value) {
335
return("# Write cache is enabled (default) for lun $lun.");
337
return("# WARNING! Unknown value ($param_value) to write-cache! Accepted values are \"on\" and \"off\".");
341
if ($param eq "scsi_id" || $param eq "scsi_sn" || $param eq "vendor_id" || $param eq "product_id" ||
342
$param eq "product_rev" || $param eq "sense_format" || $param eq "removable" || $param eq "online" ||
343
$param eq "path" || $param eq "mode_page") {
344
return("tgtadm --lld $driver --op update --mode logicalunit --tid $next_tid --lun=$lun --params $param=\"$param_value\"");
346
if ($param eq "params") {
347
return("tgtadm --lld $driver --op update --mode logicalunit --tid $next_tid --lun=$lun --params \"$param_value\"");
352
# Find next available LUN
354
my $backing_store = $_[0];
355
my $data_key_ref = $_[1];
356
my $lun_collision = 0;
359
while ($lun_is_free == 0) {
360
foreach my $testlun (keys %$data_key_ref) {
361
foreach my $testlun2 (values %{$$data_key_ref{$testlun}}) {
362
if ($found_lun eq $testlun2) {
367
if ($lun_collision == 0) {
374
$$data_key_ref{$backing_store}{'lun'} = $found_lun;
378
# Add backing or direct store
379
sub add_backing_direct {
380
my $backing_store = $_[0];
381
my $target_options_ref = $_[1];
382
my $data_key_ref = $_[2];
383
my $direct_store = $_[3];
384
my $driver = $$target_options_ref{"driver"};
386
# Is the device in use?
388
if ($force != 1 && $$target_options_ref{'allow-in-use'} ne "yes") {
389
$can_alloc = check_device($backing_store,$data_key_ref);
392
if (-e $backing_store && ! -d $backing_store && $can_alloc == 1) {
398
# Find out LUNs which are "reserved" in the config file
399
if (ref $value eq "HASH") {
400
if (length $$data_key_ref{$backing_store}{'lun'}) {
401
$lun = $$data_key_ref{$backing_store}{'lun'};
403
# Find an available lun if it wasn't specified
404
$lun = find_next_lun($backing_store,$data_key_ref);
407
# Process parameters for each lun / backing store
408
if (ref $value eq "HASH") {
412
foreach my $store (keys %$value) {
414
if (length $$target_options_ref{"lun"}) {
415
check_if_hash_array($$target_options_ref{"lun"}, "lun");
416
$lun = $$target_options_ref{"lun"};
419
if (ref $$value{$store} eq "HASH" && $store eq $backing_store) {
420
foreach my $store_option (keys %{$$value{$store}}) {
421
my $result = $$value{$store}{$store_option};
422
check_value($result);
423
if ($store_option ne "mode_page" && $store_option ne "params")
424
{ check_if_hash_array($result,$store_option) }
425
# write-cache can be set globally per target and overridden per lun,
426
# so we treat it differently
427
if ($store_option ne "mode_page" && $store_option ne "write-cache"
428
&& $store_option ne "params") {
429
my $exec_command = add_params($store_option, $result, $lun, $driver);
430
push(@exec_commands, $exec_command);
431
$params_added{$store_option} = 1;
433
if ($store_option eq "write-cache") {
434
my $exec_command = add_params($store_option, $result, $lun, $driver);
435
$params_added{write_cache} = 1;
436
push(@exec_commands, $exec_command);
438
if ($store_option eq "device-type") {
439
$device_type = $result;
440
$params_added{$store_option} = 1;
442
if ($store_option eq "bs-type") {
444
$params_added{$store_option} = 1;
446
if ($store_option eq "mode_page") {
447
unless (ref($result) eq 'ARRAY') {
448
$result = [ $result ];
450
@mode_page = @$result;
451
foreach my $mode_page (@mode_page) {
452
my $exec_command = add_params($store_option, $mode_page, $lun, $driver);
453
push(@exec_commands, $exec_command);
456
if ($store_option eq "params") {
457
unless (ref($result) eq 'ARRAY') {
458
$result = [ $result ];
461
foreach my $param (@params) {
462
my $exec_command = add_params($store_option, $param, $lun, $driver);
463
push(@exec_commands, $exec_command);
469
# Used only if lun is a direct-store
472
if ($direct_store == 1) {
473
$sg_inq=`sg_inq $backing_store`;
475
Vendor\ identification:\s+?(.*?)\n
476
\s+Product\ identification:\s+(.*?)\n
477
\s+Product\ revision\ level:\s+(.*?)\n
478
(?:\s+Unit\ serial\ number:\s+(.*?)\n)?
480
# If they were not defined globally for a target,
482
if (not length $$target_options_ref{vendor_id}) {
483
$direct_params{vendor_id} = $1;
485
if (not length $$target_options_ref{product_id}) {
486
$direct_params{product_id} = $2;
488
if (not length $$target_options_ref{product_rev}) {
489
$direct_params{product_rev} = $3;
491
if (not length $$target_options_ref{scsi_sn}) {
492
$direct_params{scsi_sn} = $4;
497
# Add these parameters if they were not overwritten in the config file
498
my @opts = ("scsi_id", "sense_format", "removable", "online", "path");
499
foreach my $single_opt (@opts) {
500
check_if_hash_array($$target_options_ref{$single_opt},$single_opt);
501
if ($params_added{$single_opt} ne 1 && length $$target_options_ref{$single_opt}) {
502
my $exec_command = add_params($single_opt, $$target_options_ref{$single_opt}, $lun, $driver);
503
push(@exec_commands, $exec_command);
504
$params_added{$single_opt} = 1;
507
# These options can be fetched by sg_inq for direct-store
508
my @opts = ("vendor_id", "product_id", "product_rev", "scsi_sn");
509
foreach my $single_opt (@opts) {
510
check_if_hash_array($$target_options_ref{$single_opt},$single_opt);
512
if (length $$target_options_ref{$single_opt}) {
513
$this_opt = $$target_options_ref{$single_opt};
514
} elsif (length $direct_params{$single_opt}) {
515
$this_opt = $direct_params{$single_opt};
517
if ($params_added{$single_opt} ne 1 && length $this_opt) {
518
my $exec_command = add_params($single_opt, $this_opt, $lun, $driver);
519
push(@exec_commands, $exec_command);
520
$params_added{$single_opt} = 1;
524
if ($params_added{write_cache} ne 1) {
525
my $exec_command = add_params("write-cache", $$target_options_ref{"write-cache"}, $lun, $driver);
526
push(@exec_commands, $exec_command);
527
$params_added{write_cache} = 1;
530
unless (ref($$target_options_ref{mode_page}) eq 'ARRAY') {
531
$$target_options_ref{mode_page} = [ $$target_options_ref{mode_page} ];
533
foreach my $mode_page (@{$$target_options_ref{"mode_page"}}) {
534
if (length $mode_page) {
535
my $exec_command = add_params("mode_page", $mode_page, $lun, $driver);
536
push(@exec_commands, $exec_command);
540
unless (ref($$target_options_ref{params}) eq 'ARRAY') {
541
$$target_options_ref{params} = [ $$target_options_ref{params} ];
543
foreach my $param (@{$$target_options_ref{"params"}}) {
545
my $exec_command = add_params("params", $param, $lun, $driver);
546
push(@exec_commands, $exec_command);
550
if ($params_added{"device-type"} ne 1) {
551
check_if_hash_array($$target_options_ref{"device-type"}, "device-type");
552
$device_type = $$target_options_ref{"device-type"};
555
if ($params_added{"bs-type"} ne 1) {
556
check_if_hash_array($$target_options_ref{"bs-type"}, "bs-type");
557
$bs_type = $$target_options_ref{"bs-type"};
560
print "If you got here, this means your config file is not supported.\n";
561
print "Please report it to stgt mailing list and attach your config files.\n";
564
# Execute commands for a given LUN
565
if (length $device_type) { $device_type = "--device-type $device_type" };
566
if (length $bs_type) { $bs_type = "--bstype $bs_type" };
567
execute("tgtadm --lld $driver --op new --mode logicalunit --tid $next_tid --lun $lun -b $backing_store $device_type $bs_type");
569
# Commands should be executed in order
571
foreach my $exec_command (@exec_commands) {
572
if (length $exec_command) {
573
if ($exec_command =~ m/params mode_page/) {
574
execute($exec_command);
576
push(@execute_last, $exec_command);
580
foreach my $exec_command (@execute_last) {
581
execute($exec_command);
585
} elsif ($can_alloc == 0) {
586
execute("# Skipping device $backing_store - it is in use.");
587
execute("# You can override it with --force or 'allow-in-use yes' config option.");
588
execute("# Note - do so only if you know what you're doing, you may damage your data.");
590
execute("# Skipping device: $backing_store");
591
execute("# $backing_store does not exist - please check the configuration file");
595
# Process options from the config file
596
sub process_options {
597
my $target_options_ref = $_[0];
598
my $data_key_ref = $_[1];
599
my $driver = $$target_options_ref{"driver"};
600
if ($option eq "backing-store" || $option eq "direct-store") {
601
my $direct_store = 0;
602
if ($option eq "direct-store") {
603
check_exe("sg_inq", "option direct-store");
607
# We want to make everything a hash to use it
608
# in the same way later on
609
unless (ref($value)) {
614
if (ref($value) eq "ARRAY") {
615
foreach my $backing_store (@$value) {
616
$arrvalue{$backing_store} = 1;
621
if (ref($value) eq "HASH") {
622
foreach my $backing_store (sort keys %$value) {
623
if ($backing_store =~ m/HASH/) {
624
print "\nYour config file is not supported. See targets.conf.example for details.\n";
628
foreach my $backing_store (sort keys %$value) {
629
add_backing_direct($backing_store,$target_options_ref,$data_key_ref,$direct_store);
634
if ($option eq "incominguser") {
635
# if we have one command, force it to be an array anyway
637
my @value_arr = @$value;
638
foreach my $incominguser (@value_arr) {
639
my @userpass = split(/ /, $incominguser);
640
check_value($userpass[1]);
641
execute("tgtadm --lld $driver --mode account --op delete --user=$userpass[0]");
642
execute("tgtadm --lld $driver --mode account --op new --user=$userpass[0] --password=$userpass[1]");
643
execute("tgtadm --lld $driver --mode account --op bind --tid=$next_tid --user=$userpass[0]");
647
if ($option eq "outgoinguser") {
648
# if we have one command, force it to be an array anyway
650
if (length @$value[1]) {
651
execute("# Warning: only one $option is allowed. Will only use the first one.");
653
my @userpass = split(/ /, @$value[0]);
654
check_value($userpass[1]);
655
execute("tgtadm --lld $driver --mode account --op delete --user=$userpass[0]");
656
execute("tgtadm --lld $driver --mode account --op new --user=$userpass[0] --password=$userpass[1]");
657
execute("tgtadm --lld $driver --mode account --op bind --tid=$next_tid --user=$userpass[0] --outgoing");
660
if ($option eq "initiator-address") {
661
# if we have one command, force it to be an array anyway
663
my @value_arr = @$value;
664
foreach my $initiator_address (@value_arr) {
665
check_value($initiator_address);
666
execute("tgtadm --lld $driver --op bind --mode target --tid $next_tid -I $initiator_address");
670
if ($option =~ m/^MaxRecvDataSegmentLength$|
671
^MaxXmitDataSegmentLength$|
680
^DataSequenceInOrder$|
681
^ErrorRecoveryLevel$|
685
^DefaultTime2Retain$|
689
# if we have one command, force it to be an array anyway
691
if (length @$value[1]) {
692
execute("# Warning: only one $option is allowed. Will only use the first one.");
694
check_value($option);
695
execute("tgtadm --lld $driver --mode target --op update --tid $next_tid --name $option --value $$value[0]");
700
# If the target is configured, but not present in the config file,
705
my @all_targets = keys %tgtadm_output_tid;
707
foreach my $existing_target (@all_targets) {
710
foreach my $k (sort keys %conf) {
711
if ( $k eq "target" ) {
712
foreach $k2 (sort keys %{$conf{$k}}) {
713
if ( $k2 eq $existing_target ) {
718
if ( $dontremove == 0 ) {
720
main_delete($existing_target);
727
# Dump current tgtd configuration
732
my @all_targets = keys %tgtadm_output_tid;
734
# If all targets use the same driver, use it only once in the config
736
my @drivers_combined;
737
foreach my $current_target (@all_targets) {
738
my $driver = show_target_info($current_target, "driver");
739
push (@drivers_combined, $driver);
743
@drivers_uniq{@drivers_combined} = ();
744
my @drivers_combined_uniq = sort keys %drivers_uniq;
746
if (scalar @drivers_combined_uniq == 1) {
747
print "default-driver $drivers_combined_uniq[0]\n\n";
750
# Print everything else in the config
751
foreach my $current_target (@all_targets) {
752
my $target_name = show_target_info($current_target, "target_name");
753
print "<target $target_name>\n";
755
if (scalar @drivers_combined_uniq gt 1) {
756
my $driver = show_target_info($current_target, "driver");
757
print "\tdriver $driver\n";
760
my @backing_stores = show_target_info($current_target, "backing_stores");
761
foreach my $backing_store (@backing_stores) {
762
print "\tbacking-store $backing_store\n";
765
my @account_information = show_target_info($current_target, "account_information");
766
foreach my $account (@account_information) {
767
if ($account =~ /(.+)\ \(outgoing\)/) {
768
print "\toutgoinguser $1 PLEASE_CORRECT_THE_PASSWORD\n";
769
} elsif (length $account) {
770
print "\tincominguser $account PLEASE_CORRECT_THE_PASSWORD\n";
774
my @acl_information = show_target_info($current_target, "acl_information");
775
if (scalar(@acl_information) != 1 || $acl_information[0] ne "ALL") {
776
foreach my $ini_address (@acl_information) {
777
print "\tinitiator-address $ini_address\n";
780
print "</target>\n\n";
784
# Offline or ready targets
785
sub ready_offline_targets {
786
my $var = $_[0]; # This variable is either "offline" or "ready"
789
$off_ready = $offline
790
} elsif ($offline eq 0) {
793
print "Invalid value (you can't use both ready and offline)!\n";
796
if ($off_ready eq "help") {
798
--$var <value> $var all or selected targets
801
--$var help - display this help
802
--$var ALL - $var all targets
803
--$var tid=4 - $var target 4 (target with tid 4)
804
--$var iqn.2008-08.com.example:some.target - $var this target
807
} elsif ($off_ready eq "ALL") {
809
# Run over all targets and offline/ready them
810
my @all_targets = keys %tgtadm_output_tid;
811
foreach my $existing_target (@all_targets) {
812
execute("tgtadm --op update --mode target --tid=$tgtadm_output_tid{$existing_target} -n state -v $var");
814
} elsif ($off_ready =~ m/tid=(.+)/) {
816
execute("tgtadm --op update --mode target --tid=$1 -n state -v $var");
819
if (length $tgtadm_output_tid{$off_ready}) {
820
execute("tgtadm --op update --mode target --tid=$tgtadm_output_tid{$off_ready} --name=\"$off_ready\" -n state -v $var");
822
print "There is no target with name \"$off_ready\", can't $var it!\n";
828
# Show info for a given target
829
sub show_target_info {
830
my $existing_target = $_[0];
832
# Returns target information
833
if ($task eq "target_name") {
834
if ($tgtadm_output{$existing_target} =~ m/^Target (\d*): (.+)/ ) {
837
# Returns driver information
838
} elsif ($task eq "driver") {
839
if ($tgtadm_output{$existing_target} =~ m/\s+Driver: (.+)/ ) {
842
# Returns backing store
843
} elsif ($task eq "backing_stores") {
844
if ($tgtadm_output{$existing_target} =~ m/\s+Backing store path: (?!None)(.+)/ ) {
845
my @backing_stores = $tgtadm_output{$existing_target} =~ m{\s+Backing store path: (?!None\n)(.+)}g;
846
return @backing_stores;
849
# Returns account information:
850
} elsif ($task eq "account_information") {
851
if ($tgtadm_output{$existing_target} =~ m{
852
\s+Account\ information:\n(.*)\n\s+ACL\ information:
855
my @accounts = split(/\n/, $1);
856
my @account_information;
857
foreach my $user (@accounts) {
858
my @var = split(/^\s+/, $user);
859
push(@account_information, $var[1]);
861
return @account_information;
863
# Returns ACL information
864
} elsif ($task eq "acl_information") {
865
if ($tgtadm_output{$existing_target} =~ m{
866
\s+ACL\ information:\n(.*)
869
my @ini_addresses = split(/\n/, $1);
871
foreach my $ini_address (@ini_addresses) {
872
my @var = split(/^\s+/, $ini_address);
873
push(@acls, $var[1]);
878
} elsif ($task eq "sessions") {
880
if ($tgtadm_output{$existing_target} =~ m{
881
\s+I_T\ nexus\ information:\n(.*)LUN\ information:
884
my @var = split(/\n/, $1);
888
foreach my $line (@var) {
889
if ($line =~ m/\s+I_T nexus:\ (.+)/) {
892
if ($line =~ m/\s+Connection:\ (.+)/) {
894
$sessions{$sid} = $cid;
903
# Main subroutine for deleting targets
905
my $current_target = $_[0];
906
my $current_tid = $_[1];
907
my $configured = check_configured($current_target);
909
# Check if the target has initiators connected
910
if ($tgtadm_output{$current_target} =~ m/\s+Connection:/) {
912
execute("# Removing target: $current_target");
914
my @acl_info = show_target_info($current_target, "acl_information");
915
foreach my $acl (@acl_info) {
916
execute("tgtadm --op unbind --mode target --tid $tgtadm_output_tid{$current_target} -I $acl");
918
# Now, remove all sessions / connections from that tid
919
my %sessions = show_target_info($current_target, "sessions");
920
foreach my $sid (keys %sessions) {
921
foreach my $cid ($sessions{$sid}) {
922
execute("tgtadm --op delete --mode conn --tid $tgtadm_output_tid{$current_target} --sid $sid --cid $cid");
925
execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$current_target}");
928
$del_upd_text = "updated";
930
$del_upd_text = "deleted";
932
execute("# Target with tid $tgtadm_output_tid{$current_target} ($current_target) is in use, it won't be $del_upd_text.");
935
} elsif (length $tgtadm_output_tid{$current_target}) {
936
execute("# Removing target: $current_target");
937
execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$current_target}");
939
if (length $current_tid) {
940
execute("# Target with tid $current_tid does not exist!");
942
execute("# Target with name $current_target does not exist!");
945
if ($configured ne 0) {
954
if ($delete eq "help") {
956
--delete <value> delete all or selected targets
957
The target will be deleted only if it's not used
958
(no initiator is connected to it).
959
If you want to delete targets which are in use,
960
you have to add "--force" flag.
963
--delete help - display this help
964
--delete ALL - delete all targets
965
--delete tid=4 - delete target 4 (target with tid 4)
966
--delete iqn.2008-08.com.example:some.target - delete this target
970
} elsif ($delete eq "ALL") {
972
# Run over all targets and delete them
973
my @all_targets = keys %tgtadm_output_tid;
974
foreach my $current_target (@all_targets) {
975
main_delete($current_target);
977
} elsif ($delete =~ m/^tid=(.+)/) {
980
my $current_target = $tgtadm_output_name{$1};
981
main_delete($current_target, $1);
985
my $current_target = $delete;
986
main_delete($current_target);
992
if ($update eq "help") {
994
--update <value> update all or selected targets
995
The target will be updated only if it's not used
996
(no initiator is connected to it).
997
If you want to update targets which are in use,
998
you have to add "--force" flag.
1001
--update help - display this help
1002
--update ALL - update all targets
1003
--update tid=4 - update target 4 (target with tid 4)
1004
--update iqn.2008-08.com.example:some.target - update this target
1008
} elsif ($update eq "ALL") {
1009
# Run over all targets and delete them if they are not in use
1012
my @targets_combined = combine_targets();
1013
foreach my $current_target (@targets_combined) {
1014
my $configured = check_configured($current_target);
1015
my $connected = check_connected($current_target);
1016
my $in_configfile = check_in_configfile($current_target);
1018
if (($in_configfile == 0) && ($configured == 1)) {
1019
# Delete the target if it's not in the config file
1020
main_delete($current_target);
1022
add_targets($current_target, $configured, $connected, $in_configfile);
1026
} elsif ($update =~ m/^tid=(.+)/) {
1030
my $current_target = $tgtadm_output_name{$1};
1031
my $configured = check_configured($current_target);
1032
my $connected = check_connected($current_target);
1033
my $in_configfile = check_in_configfile($current_target);
1034
if (($in_configfile == 0) && ($configured == 1)) {
1035
# Delete the target if it's not in the config file
1036
main_delete($current_target);
1037
} elsif ($configured == 1) {
1038
add_targets($current_target, $configured, $connected, $in_configfile);
1040
print "There is no target with tid $1, can't continue!\n";
1047
my $current_target = $update;
1048
my $configured = check_configured($current_target);
1049
my $connected = check_connected($current_target);
1050
my $in_configfile = check_in_configfile($current_target);
1051
if ($in_configfile == 0 && $configured == 1) {
1052
# Delete the target if it's not in the config file
1053
main_delete($current_target);
1055
add_targets($current_target, $configured, $connected, $in_configfile);
1060
# Find the biggest tid
1063
my @all_targets = keys %tgtadm_output_tid;
1065
foreach my $var (@all_targets) {
1066
if ($tgtadm_output_tid{$var} > $maxtid) {
1067
$maxtid = $tgtadm_output_tid{$var};
1073
# Combine targets from the config file and currently configured targets
1074
sub combine_targets {
1075
my @targets_in_configfile;
1076
my @all_targets = keys %tgtadm_output_tid;
1077
my @targets_combined;
1078
# Make an array of targets in the config file
1079
foreach my $k (sort keys %conf) {
1080
if ( $k eq "target" ) {
1081
foreach my $k2 (sort keys %{$conf{$k}}) {
1082
push(@targets_in_configfile, $k2)
1086
# Use only unique elements from both arrays
1087
foreach my $current_target (@all_targets) {
1088
push (@targets_combined, $current_target) unless grep { $_ eq $current_target } @targets_in_configfile;
1090
@targets_combined = (@targets_combined, @targets_in_configfile);
1091
return @targets_combined;
1094
# Check if a value is correct
1096
if ( not defined $_[0] or not length $_[0] ) {
1097
print "\nOption $option has a missing value!\n";
1098
print "Check your config file for errors (target: $target)\n";
1103
# Check if the target is in the config file
1104
sub check_in_configfile {
1105
my $current_target = $_[0];
1107
foreach my $k (sort keys %conf) {
1108
if ( $k eq "target" ) {
1109
foreach my $k2 (sort keys %{$conf{$k}}) {
1110
if ($k2 eq $current_target) {
1114
# If we're here, we didn't find a match
1120
# Check if the target is configured in tgtd
1121
sub check_configured {
1122
my $current_target = $_[0];
1123
if (length $tgtadm_output_tid{$current_target}) {
1130
# Check if any initiators are connected to the target
1131
sub check_connected {
1132
my $current_target = $_[0];
1133
if ($tgtadm_output{$current_target} =~ m/\s+Connection:/) {
1140
# Check if a device can be allocated
1141
# Device can be used "by system" (i.e. mounted, used as swap, as a part of
1142
# a RAID array etc.) or "by user" - i.e., already by tgtd, or someone doing:
1143
# dd if=/dev/1st_device of=/dev/2nd_device
1144
# We shouldn't allow a device to be used more than one time, as it could
1145
# cause corruption when written several times. Unless the user really wants to.
1147
my $backing_store = $_[0];
1148
my $data_key_ref = $_[1];
1150
# If allow-in-use is "yes", there is no need to do
1152
if ($$data_key_ref{$backing_store}{'allow-in-use'} eq "yes") {
1156
# Check if the system uses this device
1157
use Fcntl qw(O_RDONLY O_EXCL);
1159
sysopen(FH, $backing_store, O_RDONLY | O_EXCL);
1161
execute("# Device $backing_store is used by the system (mounted, used by swap?).");
1166
# Check if userspace uses this device
1167
my $lsof_check = check_exe("lsof");
1168
if ($lsof_check ne 1) {
1169
system("lsof $backing_store &>/dev/null");
1170
my $exit_value = $? >> 8;
1171
if ($exit_value eq 0) {
1172
execute("# Device $backing_store is used (already tgtd target?).");
1173
execute("# Run 'lsof $backing_store' to see the details.");
1180
# Execute or just print (or both) everything we start or would start
1182
if ($pretend == 0) {
1185
if ($verbose == 1) {
1188
# Don't try to execute if it's a comment
1189
my @execargs = split(/#/, $args);
1190
if ($execargs[0] ne undef) {
1193
# If non-zero exit code was return, exit
1194
my $exit_value = $? >> 8;
1195
if (($exit_value != 0) && ($ignore_errors == 0)) {
1196
print "Command:\n\t$args\nexited with code: $exit_value.\n";
1201
} elsif ($pretend == 1) {
1206
if ($execute == 1) {
1211
} elsif ($delete ne 0) {
1213
} elsif ($update ne 0) {
1215
} elsif ($dump == 1) {
1217
} elsif ($offline ne 0) {
1218
ready_offline_targets("offline");
1219
} elsif ($ready ne 0) {
1220
ready_offline_targets("ready");
1222
print "No action specified.\n";