Copyrights updated
[mirror/scst/.git] / scstadmin / scstadmin
1 #!/usr/bin/perl
2 $Version  = 'SCST Configurator v1.0.11';
3
4 # Configures SCST
5 #
6 # Author:       Mark R. Buechler
7 # License:      GPLv2
8 # Copyright (c) 2005-2009 Mark R. Buechler
9
10 sub usage
11   {
12     die <<"EndUsage";
13 $Version
14
15 Usage:
16 General Operations
17      -config <config>     : Configure SCST given the specified configuration file.
18      -ClearConfig         : Clear all SCST configuration.
19      -WriteConfig <file>  : Writes the current configuration out to the specified file.
20      -checkConfig <file>  : Checks the saved configuration in the specified file.
21      -sessions            : List current initiator sessions.
22      
23 Target Driver Operations
24      -enable <wwn|host>   : Enable target mode for driver at specified WWN or host.
25      -disable <wwn|host>  : Disable target mode for driver at specified WWN or host.
26      -issuelip            : Issue LIP on all target-enabled FC fabrics.
27
28 Device Operations
29      -adddev <device>     : Adds a device to a handler.
30          -handler <handler>
31          -path <path>
32          -options <options>
33          -blocksize <bytes>
34      -resyncdev <device>  : Resync the size of a device with the initiator(s).
35          -handler <handler>
36      -RemoveDev <device>  : Remove a device from a handler.
37          -handler <handler>
38      -SetT10DeviceId <id> : Sets the T10 Device ID of a device.
39          -device <device>
40          -handler <handler>
41  
42 User Operations
43      -adduser <user>      : Adds a user to a security group.
44          -group <group>
45      -MoveUser <user>     : Moves a user from one security group to another.
46          -group <group 1>
47          -to <group 2>
48      -RemoveUser <user>   : Delete a user from a security group.
49          -group <group>
50      -ClearUsers          : Clear all users from a given security group.
51          -group <group>
52
53 Group Operations
54      -addgroup <group>    : Add a given group to available security groups.
55      -renamegroup <group> : Renames a give group to a new name.
56          -to <new group>
57      -RemoveGroup <group> : Remove a give group from available security groups.
58      
59 Assignment Operations
60      -assigndev <device>  : Assign a given device to a security group.
61          -group <group>
62          -lun <lun>
63          -options <options>
64      -ReplaceDev <new dev>: Replaces a device assigned to a give LUN and group.
65          -group <group>
66          -lun <lun>
67          -options <options>
68      -ReleaseDev <device> : Remove a given device from a security group.
69          -group <group>
70      -ClearDevs           : Clear all device assignments for a security group.
71          -group <group>
72
73 Options
74      -ForceConfig         : Force all configuration changes, even deletions (DANGER!).
75      -noprompt            : Do not prompt or pause. Use with caution!
76   
77 Debugging (limited support)
78      -debug               : Debug mode - don\'t do anything destructive.
79
80 Available Handlers:
81       disk, vdisk, disk_perf, cdrom, vcdrom, changer, modisk, modisk_perf, tape, tape_perf
82
83 Available Options for create and open:
84       WRITE_THROUGH, READ_ONLY, O_DIRECT, NULLIO, NV_CACHE, BLOCK_IO, REMOVABLE
85
86 Available Options for assign and replace:
87       READ_ONLY
88
89 Examples:
90      Enable target mode for fibre card specifying its WWN
91        scstadmin -enable 50:06:0B:00:00:39:71:78
92
93      Disable target mode for SCSI host specifying host number
94        scstadmin -disable host4 
95
96      Create a new security group:
97        scstadmin -addgroup HOST01
98        
99      Create a device given an already existing disk file:
100        scstadmin -adddev DISK01 -handler vdisk -path /vdisks/disk01.dsk -options READ_ONLY,WRITE_THROUGH
101
102      Setting the T10 Device ID of a device
103        scstadmin -SetT10DeviceId test_disk -device disk1 -handler vdisk
104        
105      Assign a device to a security group:
106        scstadmin -assigndev DISK01 -group HOST01 -lun 1
107
108      Rename a security group:
109        scstadmin -RenameGroup HOST01 -to SERVER01
110
111      Tell all initiators to rescan LUNs:
112        scstadmin -issuelip
113
114 EndUsage
115   }
116
117 use SCST::SCST;
118 use Getopt::Long;
119 use IO::File;
120 use IO::Dir;
121 use POSIX;
122 use strict;
123
124 my $_DEF_CONFIG_ = '/etc/scst.conf';
125
126 my $TRUE  = 1;
127 my $FALSE = 0;
128
129 my $_MAX_LUNS_       = 255;
130 my $_DEFAULT_GROUP_  = 'Default';
131
132 my $_SCSI_CLASS_     = '/sys/class/scsi_host';
133 my $_FC_CLASS_       = '/sys/class/fc_host';
134 my $_SCSI_ISP_       = '/proc/scsi/isp';
135 my $_SCSITGT_QLAISP_ = '/proc/scsi_tgt/qla_isp';
136
137 my $_TGT_DEF_PREFIX_ = 'Default_';
138 my $_TGT_TMP_PREFIX_ = 'TMP_GRP';
139
140 my $SCST;
141 my $DEVICES;
142 my $TARGETS;
143 my %USERS;
144 my %ASSIGNMENTS;
145 my %HANDLERS;
146 my %GROUPS;
147 my $_DEBUG_;
148
149 my %_HANDLER_MAP_ = ('cdrom' => $SCST::SCST::CDROM_TYPE,
150                      'changer' => $SCST::SCST::CHANGER_TYPE,
151                      'disk' => $SCST::SCST::DISK_TYPE,
152                      'vdisk' => $SCST::SCST::VDISK_TYPE,
153                      'vcdrom' => $SCST::SCST::VCDROM_TYPE,
154                      'disk_perf' => $SCST::SCST::DISKPERF_TYPE,
155                      'modisk' => $SCST::SCST::MODISK_TYPE,
156                      'modisk_perf' => $SCST::SCST::MODISKPERF_TYPE,
157                      'tape' => $SCST::SCST::TAPE_TYPE,
158                      'tape_perf' => $SCST::SCST::TAPEPERF_TYPE,
159                      'processor' => $SCST::SCST::PROCESSOR_TYPE,
160                 # Add in the dev_ names as well
161                      'dev_cdrom' => $SCST::SCST::CDROM_TYPE,
162                      'dev_changer' => $SCST::SCST::CHANGER_TYPE,
163                      'dev_disk' => $SCST::SCST::DISK_TYPE,
164                      'dev_disk_perf' => $SCST::SCST::DISKPERF_TYPE,
165                      'dev_modisk' => $SCST::SCST::MODISK_TYPE,
166                      'dev_modisk_perf' => $SCST::SCST::MODISKPERF_TYPE,
167                      'dev_tape' => $SCST::SCST::TAPE_TYPE,
168                      'dev_tape_perf' => $SCST::SCST::TAPEPERF_TYPE,
169                      'dev_processor' => $SCST::SCST::PROCESSOR_TYPE);
170                      
171 my %_REVERSE_MAP_ = ($SCST::SCST::CDROM_TYPE => 'cdrom',
172                      $SCST::SCST::CHANGER_TYPE => 'changer',
173                      $SCST::SCST::DISK_TYPE => 'disk',
174                      $SCST::SCST::VDISK_TYPE => 'vdisk',
175                      $SCST::SCST::VCDROM_TYPE => 'vcdrom',
176                      $SCST::SCST::DISKPERF_TYPE => 'disk_perf',
177                      $SCST::SCST::MODISK_TYPE => 'modisk',
178                      $SCST::SCST::MODISKPERF_TYPE => 'modisk_perf',
179                      $SCST::SCST::TAPE_TYPE => 'tape',
180                      $SCST::SCST::TAPEPERF_TYPE => 'tape_perf',
181                      $SCST::SCST::PROCESSOR_TYPE => 'processor');
182
183 my %_HANDLER_TYPE_MAP_ = ($SCST::SCST::IOTYPE_PHYSICAL => 'physical',
184                           $SCST::SCST::IOTYPE_VIRTUAL => 'virtual',
185                           $SCST::SCST::IOTYPE_PERFORMANCE => 'performance');
186
187 $SIG{INT} = \&commitSuicide;
188
189 use vars qw($Version);
190
191 POSIX::setsid();
192
193 &main();
194
195 sub getArgs {
196         my $applyConfig;
197         my $forceConfig;
198         my $clearConfig;
199         my $writeConfig;
200         my $checkConfig;
201         my $showSessions;
202         my $addDev;
203         my $devPath;
204         my $resyncDev;
205         my $replaceDev;
206         my $removeDev;
207         my $sett10id;
208         my $addUser;
209         my $moveUser;
210         my $removeUser;
211         my $clearUsers;
212         my $addGroup;
213         my $toGroup;
214         my $renameGroup;
215         my $removeGroup;
216         my $assignDev;
217         my $replaceDev;
218         my $releaseDev;
219         my $clearDevs;
220         my $devLun;
221         my $handler;
222         my $device;
223         my $group;
224         my $options;
225         my $blocksize;
226         my $enable;
227         my $disable;
228         my $issuelip;
229         my $noprompt;
230
231         my $p = new Getopt::Long::Parser;
232
233         if (!$p->getoptions('config:s'          => \$applyConfig,
234                             'ClearConfig'       => \$clearConfig,
235                             'ForceConfig'       => \$forceConfig,
236                             'WriteConfig=s'     => \$writeConfig,
237                             'checkConfig=s'     => \$checkConfig,
238                             'sessions'          => \$showSessions,
239                             'adddev=s'          => \$addDev,
240                             'path=s'            => \$devPath,
241                             'ReplaceDev=s'      => \$replaceDev,
242                             'RemoveDev=s'       => \$removeDev,
243                             'SetT10DeviceId=s'  => \$sett10id,
244                             'lun=s'             => \$devLun,
245                             'adduser=s'         => \$addUser,
246                             'MoveUser=s'        => \$moveUser,
247                             'RemoveUser=s'      => \$removeUser,
248                             'ClearUsers'        => \$clearUsers,
249                             'addgroup=s'        => \$addGroup,
250                             'to=s'              => \$toGroup,
251                             'RemoveGroup=s'     => \$removeGroup,
252                             'renamegroup=s'     => \$renameGroup,
253                             'assigndev=s'       => \$assignDev,
254                             'resyncdev=s'       => \$resyncDev,
255                             'ReleaseDev=s'      => \$releaseDev,
256                             'ClearDevs'         => \$clearDevs,
257                             'handler=s'         => \$handler,
258                             'device=s'          => \$device,
259                             'group=s'           => \$group,
260                             'options=s'         => \$options,
261                             'blocksize=s'       => \$blocksize,
262                             'enable=s'          => \$enable,
263                             'disable=s'         => \$disable,
264                             'issuelip'          => \$issuelip,
265                             'noprompt'          => \$noprompt,
266                             'debug'             => \$_DEBUG_)) {
267                 &usage();
268         }
269
270         if ((defined($enable) && !$enable) || (defined($disable) && !$disable)) {
271                 print "Argument -enable/-disable requires a WWN or host.\n\n";
272                 usage();
273         }
274
275         if (defined($issuelip) && ($enable || $disable)) {
276                 print "Argument -issuelip cannot be used with -enable or -disable.\n\n";
277                 usage();
278         }
279
280         if ($handler && !$_HANDLER_MAP_{$handler}) {
281                 print "Invalid handler '$handler' specified. Available handlers are:\n\n";
282                 foreach my $_handler (keys %_HANDLER_MAP_) {
283                         print "\t$_handler\n";
284                 }
285                 print "\n";
286                 exit 1;
287         }
288
289         if ($addDev && !($handler && $devPath)) {
290                 print "Please specify -handler and -path with -adddev.\n\n";
291                 usage();
292         }
293
294         if (defined($blocksize) && !$blocksize) {
295                 print "Please specify bytes with -blocksize.\n\n";
296                 usage();
297         }
298
299         if ($blocksize && !$addDev) {
300                 print "Please specify -adddev with -blocksize.\n";
301                 usage();
302         }
303
304         if (defined($forceConfig) && !defined($applyConfig)) {
305                 print "Please specify -config with -ForceConfig.\n\n";
306                 usage();
307         }
308
309         if ($resyncDev && !$handler) {
310                 print "Please specify -handler with -resyncdev.\n\n";
311                 usage();
312         }
313
314         if ($removeDev && !$handler) {
315                 print "Please specify -handler with -RemoveDev.\n\n";
316                 usage();
317         }
318
319         if ($addUser && !defined($group)) {
320                 print "Please specify -group with -adduser.\n\n";
321                 usage();
322         }
323
324         if ($moveUser && (!defined($group) || !defined($toGroup))) {
325                 print "Please specify -group and -to with -MoveUser.\n\n";
326                 usage();
327         }
328
329         if ($removeUser && !defined($group)) {
330                 print "Please specify -group with -RemoveUser.\n\n";
331                 usage();
332         }
333
334         if ($clearUsers && !defined($group)) {
335                 print "Please specify -group with -ClearUsers.\n\n";
336                 usage();
337         }
338
339         if ($renameGroup && !defined($toGroup)) {
340                 print "Please specify -to with -renamegroup.\n\n";
341                 usage();
342         }
343
344         if ($assignDev && !defined($group)) {
345                 print "Please specify -group with -assigndev.\n\n";
346                 usage();
347         }
348
349         if ($replaceDev && (!defined($group) || !defined($devLun))) {
350                 print "Please specify -group and -lun with -ReplaceDev.\n\n";
351                 usage();
352         }
353
354         if ($releaseDev && !defined($group)) {
355                 print "Please specify -group with -ReleaseDev.\n\n";
356                 usage();
357         }
358
359         if ($sett10id && (!$device || !$handler)) {
360                 print "Please specify -device and -handler with -SetT10DeviceId.\n\n";
361                 usage();
362         }
363
364         if ($clearDevs && !defined($group)) {
365                 print "Please specify -group with -ClearDevs.\n\n";
366                 usage();
367         }
368
369         if (defined($writeConfig) && !$writeConfig) {
370                 print "Please specify a file name to write configuration to..\n\n";
371                 usage();
372         }
373
374         $_DEBUG_ = $TRUE if (defined($_DEBUG_));
375
376         $forceConfig = $TRUE if (defined($forceConfig));
377         $showSessions = $TRUE if (defined($showSessions));
378         $issuelip = $TRUE if (defined($issuelip));
379         $noprompt = $TRUE if (defined($noprompt));
380
381         $enable =~ tr/A-Z/a-z/; $disable =~ tr/A-Z/a-z/;
382         $options =~ tr/a-z/A-Z/ if ($options);
383
384         if ((defined($showSessions) + defined($addDev) + defined($resyncDev) +
385              defined($removeDev)+ defined($addUser) + defined($enable) + defined($disable) +
386              defined($removeUser) + defined($clearUsers) + defined($assignDev) +
387              defined($releaseDev) + defined($clearDevs) + defined($applyConfig) +
388              defined($clearConfig) + defined($writeConfig) + defined($checkConfig) +
389              defined($sett10id)) > 1) {
390                 print "Please specify only one operation at a time.\n";
391                 usage();
392         }
393
394         $applyConfig = $_DEF_CONFIG_ if (defined($applyConfig) && !$applyConfig);
395         $checkConfig = $_DEF_CONFIG_ if (defined($checkConfig) && !$checkConfig);
396
397         return ($enable, $disable, $issuelip, $addDev, $devPath, $devLun, $resyncDev, $removeDev, $addUser,
398                 $moveUser, $removeUser, $clearUsers, $addGroup, $renameGroup, $toGroup, $removeGroup,
399                 $assignDev, $replaceDev, $releaseDev, $sett10id, $clearDevs, $handler, $device, $group,
400                 $options, $blocksize, $applyConfig, $forceConfig, $clearConfig, $writeConfig, $checkConfig,
401                 $showSessions, $noprompt);
402 }
403
404 sub main {
405         my $rc;
406
407         STDOUT->autoflush(1);
408
409         # We need to run as root
410         if ( $> ) {die("This program must run as root.\n");}
411
412         my ($enable, $disable, $issuelip, $addDev, $devPath, $devLun, $resyncDev, $removeDev, $addUser,
413             $moveUser, $removeUser, $clearUsers, $addGroup, $renameGroup, $toGroup, $removeGroup,
414             $assignDev, $replaceDev, $releaseDev, $sett10id, $clearDevs, $handler, $device, $group,
415             $options, $blocksize, $applyConfig, $forceConfig, $clearConfig, $writeConfig, $checkConfig,
416             $showSessions, $noprompt) = getArgs();
417
418         $SCST = new SCST::SCST($_DEBUG_);
419
420         readWorkingConfig();
421
422         SWITCH: {
423                 $applyConfig && do {
424                         if ($forceConfig) {
425                                 $rc = applyConfiguration($applyConfig, $TRUE, $TRUE);
426                                 die("Configuration errors found, aborting.\n") if ($rc);
427
428                                 if (!$noprompt) {
429                                         print "\nConfiguration will apply in 10 seconds, type ctrl-c to abort..\n";
430                                         sleep 10;
431                                 }
432                         }
433
434                         readWorkingConfig();
435                         $rc = applyConfiguration($applyConfig, $forceConfig, $FALSE);
436                         last SWITCH;
437                 };
438                 $checkConfig && do {
439                         $rc = applyConfiguration($checkConfig, $FALSE, $TRUE);
440                         last SWITCH;
441                 };
442                 $writeConfig && do {
443                         $rc = writeConfiguration($writeConfig);
444                         last SWITCH;
445                 };
446                 $showSessions && do {
447                         $rc = showSessions();
448                         last SWITCH;
449                 };
450                 defined($clearConfig) && do {
451                         $rc = clearConfiguration($noprompt);
452                         last SWITCH;
453                 };
454                 $addDev && do {
455                         $rc = addDevice($handler, $addDev, $devPath, $options, $blocksize);
456                         last SWITCH;
457                 };
458                 $resyncDev && do {
459                         $rc = resyncDevice($handler, $resyncDev);
460                         last SWITCH;
461                 };
462                 $removeDev && do {
463                         $rc = removeDevice($handler, $removeDev);
464                         last SWITCH;
465                 };
466                 $sett10id && do {
467                         $rc = setT10DeviceId($handler, $device, $sett10id);
468                         last SWITCH;
469                 };
470                 $addUser && do {
471                         $rc = addUser($group, $addUser);
472                         last SWITCH;
473                 };
474                 $moveUser && do {
475                         $rc = moveUser($group, $moveUser, $toGroup);
476                         last SWITCH;
477                 };
478                 $removeUser && do {
479                         $rc = removeUser($group, $removeUser);
480                         last SWITCH;
481                 };
482                 defined($clearUsers) && do {
483                         $rc = clearUsers($group);
484                         last SWITCH;
485                 };
486                 $addGroup && do {
487                         $rc = addGroup($addGroup);
488                         last SWITCH;
489                 };
490                 $renameGroup && do {
491                         $rc = renameGroup($renameGroup, $toGroup);
492                         last SWITCH;
493                 };
494                 $removeGroup && do {
495                         $rc = removeGroup($removeGroup);
496                         last SWITCH;
497                 };
498                 $assignDev && do {
499                         $rc = assignDevice($group, $assignDev, $devLun, $options);
500                         last SWITCH;
501                 };
502                 $replaceDev && do {
503                         $rc = replaceDevice($group, $replaceDev, $devLun, $options);
504                         last SWITCH;
505                 };
506                 $releaseDev && do {
507                         $rc = releaseDevice($group, $releaseDev);
508                         last SWITCH;
509                 };
510                 defined($clearDevs) && do {
511                         $rc = clearDevices($group);
512                         last SWITCH;
513                 };
514                 $enable && do {
515                         $enable = unformatTarget($enable);
516                         $rc = enableTarget($enable, $TRUE);
517                         last SWITCH;
518                 };
519                 $disable && do {
520                         $disable = unformatTarget($disable);
521                         $rc = enableTarget($disable, $FALSE);
522                         last SWITCH;
523                 };
524                 $issuelip && do {
525                         $rc = issueLIP();
526                         last SWITCH;
527                 };
528
529                 print "No valid operations specified.\n";
530                 usage();
531                 exit $TRUE;
532         }
533
534         print "\nAll done.\n";
535
536         exit $rc;
537 }
538
539 sub readWorkingConfig {
540         my %empty;
541
542         print "Collecting current configuration: ";
543
544         $TARGETS  = undef;
545         $DEVICES  = undef;
546         %HANDLERS = ();
547         %GROUPS   = ();
548         %USERS    = ();
549
550         my $eHandlers = $SCST->handlers();
551
552         immediateExit($SCST->errorString());
553
554         foreach my $handler (@{$eHandlers}) {
555                 $HANDLERS{$handler}++; # For quick lookups
556         }
557
558         $TARGETS = targets();
559
560         $DEVICES = $SCST->devices();
561         immediateExit($SCST->errorString());
562
563         my $_eGroups = $SCST->groups();
564         immediateExit($SCST->errorString());
565
566         foreach my $group (@{$_eGroups}) {
567                 $GROUPS{$group}++;
568                 $ASSIGNMENTS{$group} = $SCST->groupDevices($group);
569                 my $eUsers = $SCST->users($group);
570
571                 foreach my $user (@{$eUsers}) {
572                         $USERS{$group}->{$user}++; # For quick lookups
573                 }
574                 $USERS{$group} = \%empty if (!$USERS{$group});
575         }
576
577         print "done.\n\n";
578 }
579
580 sub writeConfiguration {
581         my $file = shift;
582         my $keep_config;
583
584         my $config = readConfig($file, $TRUE);
585
586         if (-f $file) {
587                 if (!unlink $file) {
588                         print "Failed to save current configuration, specified ".
589                           "file exists and cannot be deleted.\n";
590                         return 1;
591                 }
592         }
593
594         my $io = new IO::File $file, O_CREAT|O_WRONLY;
595
596         if (!$io) {
597                 print "Failed to save configuration to file '$file': $!\n";
598                 return 1;
599         }
600
601         print "Writing current configuration to file '$file'.. ";
602
603         print $io "# Automatically generated by $Version.\n\n";
604
605         print $io "# NOTE: Options are pipe (|) seperated.\n\n";
606
607         print $io "[OPTIONS]\n";
608         print $io "#OPTION <1|0|YES|NO|TRUE|FALSE|VALUE>\n";
609         print $io "# Copy configuration options during a -writeconfig\n";
610         print $io "KEEP_CONFIG ";
611
612         if (defined($config)) {
613                 $keep_config = $$config{'OPTIONS'}->{'default'}->{'KEEP_CONFIG'}[0];
614         } else {
615                 $keep_config = $FALSE;
616         }
617
618         if (!defined($config)) {
619                 print $io "TRUE\n";
620         } elsif (!$keep_config ) {
621                 print $io "FALSE\n";
622         } else {
623                 print $io "TRUE\n";
624         }
625
626         print $io "# For FC targets, issue a LIP after every assignment change\n";
627         print $io "ISSUE_LIP ";
628
629         if (!$keep_config) {
630                 print $io "FALSE\n";
631         } else {
632                 print $io ($$config{'OPTIONS'}->{'default'}->{'ISSUE_LIP'}[0] ? "TRUE\n" : "FALSE\n");
633         }
634
635         print $io "\n";         
636
637         # Device information
638         foreach my $handler (sort keys %HANDLERS) {
639                 if ($SCST->handlerType($handler) == $SCST::SCST::IOTYPE_VIRTUAL) {
640                         print $io "[HANDLER ".$_REVERSE_MAP_{$handler}."]\n";
641                         print $io "#DEVICE <vdisk name>,<device path>";
642                         if ($handler == $SCST::SCST::VDISK_TYPE) {
643                                 print $io ",<options>,<block size>,<t10 device id>\n";
644                         } else {
645                                 print $io "\n";
646                         }
647
648                         my $devices = $SCST->handlerDevices($handler);
649
650                         immediateExit($SCST->errorString());
651
652                         foreach my $device (sort keys %{$devices}) {
653                                 my $options = $$devices{$device}->{'OPTIONS'};
654
655                                 $options =~ s/\,/\|/g;
656
657                                 print $io "DEVICE $device,".$$devices{$device}->{'PATH'};
658                                 print $io ",$options";
659                                 print $io ",".$$devices{$device}->{'BLOCKSIZE'};
660                                 print $io ",".$$devices{$device}->{'T10_DEVICE_ID'};
661                                 print $io "\n";
662                         }
663
664                         print $io "\n";
665                 }
666         }
667
668         # User configuration
669         foreach my $group (sort keys %USERS) {
670                 print $io "[GROUP $group]\n";
671                 print $io "#USER <user wwn>\n";
672
673                 foreach my $user (keys %{$USERS{$group}}) {
674                         print $io "USER $user\n";
675                 }
676
677                 print $io "\n";
678         }
679
680         # Assignments configuration
681         foreach my $group (sort keys %ASSIGNMENTS) {
682                 print $io "[ASSIGNMENT $group]\n";
683                 print $io "#DEVICE <device name>,<lun>,<options>\n";
684
685                 my $pointer = $ASSIGNMENTS{$group};
686                 foreach my $device (sort keys %{$pointer}) {
687                         print $io "DEVICE $device,".$$pointer{$device}."\n";
688                 }
689
690                 print $io "\n";
691         }
692
693         # Targets configuration
694         foreach my $type ('enable', 'disable') {
695                 print $io "[TARGETS $type]\n";
696                 print $io "#HOST <wwn identifier>\n";
697
698                 foreach my $target (sort keys %{$TARGETS}) {
699                         if ((($type eq 'enable') && $$TARGETS{$target}->{'enabled'}) ||
700                             (($type eq 'disable') && !$$TARGETS{$target}->{'enabled'})) {
701                                 my $f_target = formatTarget($target);
702                                 print $io "HOST $f_target\n" if (!$$TARGETS{$target}->{'duplicate'});
703                         }
704                 }
705
706                 print $io "\n";
707         }
708
709         print "done\n";
710
711         close $io;
712
713         return 0;
714 }
715
716 sub applyConfiguration {
717         my $confile = shift;
718         my $force = shift;
719         my $check = shift;
720         my $config = readConfig($confile);
721         my $errs;
722         my $changes = 0;
723
724         my $assign_changes = 0;
725         my $targets_changed = $FALSE;
726
727         my %used_devs;
728         my %used_users;
729         my %used_assignments;
730         my %empty;
731
732         my %seen_users;
733
734         my %rename_group;
735
736         my $issue_lip = $$config{'OPTIONS'}->{'default'}->{'ISSUE_LIP'}[0];
737
738         # Cache device/handler configuration
739         foreach my $entry (keys %{$$config{'HANDLER'}}) {
740                 foreach my $device (@{$$config{'HANDLER'}->{$entry}->{'DEVICE'}}) {
741                         my($vname, undef) = split(/\,/, $device, 2);
742                         $vname = cleanupString($vname);
743                         $used_devs{$vname} = $entry;
744                 }
745         }
746
747         # Cache user/group configuration
748         foreach my $group (keys %{$$config{'GROUP'}}) {
749                 foreach my $user (@{$$config{'GROUP'}->{$group}->{'USER'}}) {
750                         if (defined($seen_users{$user})) {
751                                 print "\t-> FATAL: Configuration invalid. User '$user' is in more ".
752                                   "than one group!\n";
753                                 exit 1;
754                         }
755                         $used_users{$group}->{$user}++;
756                         $seen_users{$user}++;
757                 }
758                 $used_users{$group} = \%empty if (!$used_users{$group});
759         }
760
761         # Cache device association configuration
762         foreach my $group (keys %{$$config{'ASSIGNMENT'}}) {
763                 my %seen_luns;
764
765                 foreach my $device (@{$$config{'ASSIGNMENT'}->{$group}->{'DEVICE'}}) {
766                         my($vname, $arg) = split(/\,/, $device, 2);
767                         $vname = cleanupString($vname);
768                         my($lun, $options) = split(/\,/, $arg);
769                         if ($seen_luns{$lun}) {
770                                 print "\t-> FATAL: Configuration invalid. Group '$group' has multiple ".
771                                   "devices assigned to LUN $lun.\n";
772                                 exit 1;
773                         }
774                         $used_assignments{$group}->{$vname} = $arg;
775                         $seen_luns{$lun}++;
776                 }
777         }
778
779         # If -ForceConfig is used, check for configurations which we've deleted but are still active.
780         if ($force || $check) {
781                 # Associations
782                 foreach my $group (sort keys %ASSIGNMENTS) {
783                         if (!defined($used_assignments{$group}) && (keys %{$ASSIGNMENTS{$group}})) {
784                                 print "\t-> WARNING: Group '$group' has no associations in saved configuration";
785
786                                 if (!$check) {
787                                         print ", clearing all associations.\n";
788                                         if (clearDevices($group)) {
789                                                 $errs++;
790                                         } else {
791                                                 $changes++;
792                                                 $assign_changes++;
793                                         }
794                                 } else {
795                                         print ".\n";
796                                         $changes++;
797                                 }
798                         } else {
799                                 my $_assignments = $ASSIGNMENTS{$group};
800
801                                 foreach my $device (sort keys %{$_assignments}) {
802                                         if (!defined($used_assignments{$group}->{$device}) ||
803                                            ($$_assignments{$device} != $used_assignments{$group}->{$device})) {
804                                                 if (defined($used_assignments{$group}->{$device}) &&
805                                                     ($$_assignments{$device} != $used_assignments{$group}->{$device})) {
806                                                         print "\t-> WARNING: Device '$device' assigned to group '$group' ".
807                                                           "is at LUN ".$used_assignments{$group}->{$device}.
808                                                           " whereas working configuration reflects LUN ".$$_assignments{$device}; 
809                                                 } else {
810                                                         print "\t-> WARNING: Device '$device' is not associated with group ".
811                                                           "'$group' in saved configuration";
812                                                 }
813
814                                                 if (!$check) {
815                                                         my $_lun = $$_assignments{$device};
816                                                         my $replace_dev = findAssignedLun($used_assignments{$group}, $_lun);
817
818                                                         if (defined($replace_dev) && ($replace_dev ne $device)) {
819                                                                 print ", replacing with device '$replace_dev'.\n";
820
821                                                                 if (replaceDevice($group, $replace_dev, $_lun)) {
822                                                                         $errs++;
823                                                                 } else {
824                                                                         $changes++;
825                                                                         $assign_changes++;
826                                                                 }
827                                                         } else {
828                                                                 print ", releasing.\n";
829                                                                 if (releaseDevice($group, $device)) {
830                                                                         $errs++;
831                                                                 } else {
832                                                                         $changes++;
833                                                                         $assign_changes++;
834                                                                 }
835                                                         }
836                                                 } else {
837                                                         print ".\n";
838                                                         $changes++;
839                                                 }
840                                         }
841                                 }
842                         }
843                 }
844
845                 # Users & Groups
846                 foreach my $group (sort keys %USERS) {
847                         next if ($group eq $_DEFAULT_GROUP_);
848                         if (!defined($used_users{$group})) {
849                                 print "\t-> WARNING: Group '$group' does not exist in saved configuration";
850
851                                 if (!$check) {
852                                         print ", removing.\n";
853                                         if (clearUsers($group)) {
854                                                 $errs++;
855                                         } else {
856                                                 $changes++;
857                                         }
858
859                                         if (removeGroup($group)) {
860                                                 $errs++;
861                                         } else {
862                                                 $changes++;
863                                         }
864                                 } else {
865                                         print ".\n";
866                                         $changes++;
867                                 }
868                         } else {
869                                 foreach my $user (sort keys %{$USERS{$group}}) {
870                                         if (!defined($used_users{$group}->{$user})) {
871                                                 print "\t-> WARNING: User '$user' is not defined as part of group '$group' ".
872                                                   "in saved configuration";
873
874                                                 if (!$check) {
875                                                         # Are we moving this user to another group?
876                                                         my $new_group = findUserGroup($user, $config);
877                                                         if ($new_group && ($new_group ne $group)) {
878                                                                 print ", moving to group '$new_group'.\n";
879                                                                 if (moveUser($group, $user, $new_group)) {
880                                                                         $errs++;
881                                                                 } else {
882                                                                         $changes++;
883                                                                 }
884                                                         } else {
885                                                                 print ", removing.\n";
886                                                                 if (removeUser($group, $user)) {
887                                                                         $errs++;
888                                                                 } else {
889                                                                         $changes++;
890                                                                 }
891                                                         }
892                                                 } else {
893                                                         print ".\n";
894                                                         $changes++;
895                                                 }
896                                         }
897                                 }
898                         }
899                 }
900
901                 # Devices
902                 foreach my $device (sort keys %{$DEVICES}) {
903                         if ($$DEVICES{$device} && !defined($used_devs{$device})) {
904                                 # Device gone, but is it still assigned to a group?
905                                 my $isAssigned = $FALSE;
906                                 foreach my $group (sort keys %used_assignments) {
907                                         if (defined($used_assignments{$group}->{$device})) {
908                                                 print "\t-> WARNING: Device '$device' is not defined in saved configuration, ".
909                                                   "however, it is still assigned to group '$group'! Ignoring removal.\n";
910                                                 $isAssigned = $TRUE;
911                                         }
912                                 }
913
914                                 if (!$isAssigned && ($SCST->handlerType($$DEVICES{$device}) == $SCST::SCST::IOTYPE_VIRTUAL)) {
915                                         print "\t-> WARNING: Device '$device' is not defined in saved configuration";
916
917                                         if (!$check) {
918                                                 print ", removing.\n";
919                                                 if (removeDevice($_REVERSE_MAP_{$$DEVICES{$device}}, $device)) {
920                                                         $errs++;
921                                                 } else {
922                                                         $changes++;
923                                                 }
924                                         } else {
925                                                 print ".\n";
926                                                 $changes++;
927                                         }
928                                 }
929                         } else {
930                                 # Handler change
931                                 if ($_HANDLER_MAP_{$used_devs{$device}} != $$DEVICES{$device}) {
932                                         my $handler = $used_devs{$device};
933
934                                         if ($HANDLERS{$_HANDLER_MAP_{$handler}}) {
935                                                 print "\t-> WARNING: Device '$device' changes handler to '$handler'";
936
937                                                 if (!$check) {
938                                                         print ", changing.\n";
939                                                         if ($SCST->assignDeviceToHandler($device,
940                                                                                          $_HANDLER_MAP_{$handler})) {
941                                                                 $errs++;
942                                                         } else {
943                                                                 $changes++;
944                                                         }
945                                                 } else {
946                                                         print ".\n";
947                                                         $changes++;
948                                                 }
949                                         }
950                                 }
951                         }
952                 }
953         }
954
955         print "\nApplying configuration additions..\n" if (!$check);
956         print "\n";
957
958         readWorkingConfig() if ($force);
959
960         foreach my $_handler (sort keys %{$$config{'HANDLER'}}) {
961                 if (!$HANDLERS{$_HANDLER_MAP_{$_handler}}) {
962                         print "\t-> WARNING: Handler '$_handler' does not exist.\n";
963                         $errs += 1;
964                         next;
965                 }
966
967                 foreach my $device (@{$$config{'HANDLER'}->{$_handler}->{'DEVICE'}}) {
968                         my($vname, $path, $options, $blocksize, $t10_id) = split(/\,/, $device);
969                         $path = cleanupString($path);
970                         $options =~ s/\s+//g;
971
972                         if (defined($$DEVICES{$vname}) && ($_HANDLER_MAP_{$_handler} == $$DEVICES{$vname})) {
973                                 next;
974                         } elsif (defined($$DEVICES{$vname}) && ($_HANDLER_MAP_{$_handler} != $$DEVICES{$vname})) {
975                                 if ($HANDLERS{$_HANDLER_MAP_{$_handler}}) {
976                                         print "\t-> WARNING: Device '$vname' changes handler from '".
977                                           $_REVERSE_MAP_{$$DEVICES{$vname}}."' to '$_handler'.\n".
978                                           "\t   Use -ForceConfig to change device handler.\n" if (!$force && !$check);
979                                 }
980                                 next;
981                         }
982
983                         if ($check) {
984                                 print "\t-> New device '$_handler:$vname' at path '$path', options '$options', ".
985                                   "blocksize $blocksize.\n";
986                                 $$DEVICES{$vname} = $_HANDLER_MAP_{$_handler};
987                                 $changes++;
988                         } else {
989                                 if (addDevice($_handler, $vname, $path, $options, $blocksize)) {
990                                         $errs++;
991                                 } else {
992                                         $changes++;
993                                 }
994
995                                 if ($t10_id ne '') {
996                                         if (setT10DeviceId($_handler, $vname, $t10_id)) {
997                                                 $errs++;
998                                         } else {
999                                                 $changes++;
1000                                         }
1001                                 }
1002                         }
1003                 }
1004         }
1005
1006         # Create new groups and add users..
1007         foreach my $group (keys %used_users) {
1008                 if (!defined($USERS{$group})) {
1009                         if ($group =~ /^$_TGT_DEF_PREFIX_/) {
1010                                 my $tmp_group = randomGroup();
1011
1012                                 if (!defined($tmp_group)) {
1013                                         print "\t-> WARNING: Unable to find a free temporary group for '$group', ".
1014                                           "using the original group name instead.\n";
1015
1016                                 } else {
1017                                         print "\t-> Using temporary group '$tmp_group' for group '$group'.\n";
1018
1019                                         $rename_group{$tmp_group} = $group;
1020                                         $used_users{$tmp_group} = $used_users{$group};
1021                                         $group = $tmp_group;
1022                                 }
1023                         }
1024
1025                         if ($check) {
1026                                 print "\t-> New group definition '$group.'\n";
1027                                 $GROUPS{$group}++;
1028                                 $changes++;
1029                         } else {
1030                                 if (addGroup($group)) {
1031                                         $errs++;
1032                                 } else {
1033                                         $changes++;
1034                                 }
1035                         }
1036                 }
1037
1038                 foreach my $user (keys %{$used_users{$group}}) {
1039                         if (!defined($USERS{$group}->{$user})) {
1040                                 my $move_group = findUserGroupInCurrent($user);
1041                                 if ($move_group) {
1042                                         print "\t-> WARNING: Use -ForceConfig to move user '$user' ".
1043                                           "from group '$move_group' to group '$group'.\n" if (!$force);
1044                                         next;
1045                                 }
1046
1047                                 if ($check) {
1048                                         print "\t-> New user definition '$user' for group '$group'.\n";
1049                                         $USERS{$group}->{$user}++;
1050                                         $changes++;
1051                                 } else {
1052                                         if (addUser($group, $user)) {
1053                                                 $errs++;
1054                                         } else {
1055                                                 $changes++;
1056                                         }
1057                                 }
1058                         }
1059                 }
1060         }
1061
1062         # Assign new devices to groups..
1063         foreach my $group (keys %used_assignments) {
1064                 if (!defined($GROUPS{$group})) {
1065                         # Looks like we're lacking a group. We'll create an empty one
1066
1067                         print "\t-> WARNING: Auto-creating an empty group '$group' since none was configured.\n";
1068
1069                         if (addGroup($group)) {
1070                                 $errs++;
1071                         } else {
1072                                 $changes++;
1073                         }
1074                 }
1075
1076                 if (!defined($GROUPS{$group})) {
1077                         print "\t-> WARNING: Unable to assign to non-existant group '$group'.\n";
1078                         $errs += 1;
1079                         next;
1080                 }
1081
1082                 foreach my $vname (keys %{$used_assignments{$group}}) {
1083                         my $arg = $used_assignments{$group}->{$vname};
1084                         my($lun, $options) = split(/\,/, $arg);
1085                         my $_assignments = $ASSIGNMENTS{$group};
1086
1087                         if (defined($$_assignments{$vname}) && ($$_assignments{$vname} == $lun)) {
1088                                 next;
1089                         } elsif (defined($$_assignments{$vname}) && ($$_assignments{$vname} != $lun)) {
1090                                 print "\t-> Device '$vname' assigned to group '$group' is at LUN ".$$_assignments{$vname}.
1091                                   ", whereas the working configuration reflects LUN $lun.\n".
1092                                   "\t   Use -ForceConfig to force this LUN change.\n" if (!$force && !$check);
1093                         } else {
1094                                 my $replace_dev = findAssignedLun($_assignments, $lun);
1095
1096                                 if (defined($replace_dev) && ($vname ne $replace_dev)) {
1097                                         print "\t-> WARNING: Use -ForceConfig to replace device '$replace_dev' ".
1098                                           "with device '$vname' for group '$group'.\n" if (!$force);
1099                                         next;
1100                                 }
1101
1102                                 if ($check) {
1103                                         $lun = 'auto' if (!defined($lun));
1104                                         print "\t-> New device assignment for '$vname' to group '$group' at LUN $lun.\n";
1105                                         $changes++;
1106                                 } else {
1107                                         if (assignDevice($group, $vname, $lun, $options)) {
1108                                                 $errs++;
1109                                         } else {
1110                                                 $changes++;
1111                                                 $assign_changes++;
1112                                         }
1113                                 }
1114                         }
1115                 }
1116         }
1117
1118         foreach my $tmp_group (keys %rename_group) {
1119                 my $group = $rename_group{$tmp_group};
1120                 print "\t-> Processing temporary group '$tmp_group'.\n";
1121
1122                 renameGroup($tmp_group, $group);
1123                 $changes++;
1124         }
1125
1126         # Enable/Disable configured targets
1127         foreach my $type (keys %{$$config{'TARGETS'}}) {
1128                 my $enable;
1129
1130                 if ($type eq 'enable') {
1131                         $enable = $TRUE;
1132                 } elsif ($type eq 'disable') {
1133                         $enable = $FALSE;
1134                 } else {
1135                         print "\t-> WARNING: Ignoring invalid TARGETS specifier '$type'. ".
1136                           "Should be one of enable, disable.\n";
1137                         next;
1138                 }
1139
1140                 foreach my $target (@{$$config{'TARGETS'}->{$type}->{'HOST'}}) {
1141                         my $i_target = unformatTarget($target);
1142
1143                         if (!defined($$TARGETS{$i_target})) {
1144                                 print "\t-> WARNING: Target '$target' not found on system.\n";
1145                                 $errs += 1;
1146                                 next;
1147                         }
1148
1149                         next if ($enable == targetEnabled($i_target));
1150
1151                         if (!$enable && targetEnabled($target)) {
1152                                 if ($force || $check) {
1153                                         print "\t-> WARNING: Target mode for '$target' is currently enabled, ".
1154                                           "however configuration file wants it disabled";
1155
1156                                         if (!$check) {
1157                                                 print ", disabling.\n";
1158                                                 if (enableTarget($target, $enable)) {
1159                                                         $errs++;
1160                                                 } else {
1161                                                         $changes++;
1162                                                         $targets_changed = $TRUE;
1163                                                 }
1164                                         } else {
1165                                                 print ".\n";
1166                                                 $changes++;
1167                                         }
1168                                 }
1169                         } else {
1170                                 print "\t-> Target '$target' is enabled in configuration file, ".
1171                                   "however is currently disabled";
1172
1173                                 if (!$check) {
1174                                         print ", enabling.\n";
1175                                         if (enableTarget($target, $enable)) {
1176                                                 $errs++;
1177                                         } else {
1178                                                 $changes++;
1179                                                 $targets_changed = $TRUE;
1180                                         }
1181                                 } else {
1182                                         print ".\n";
1183                                         $changes++;
1184                                 }
1185                         }
1186                 }
1187         }
1188
1189         if ($issue_lip && $assign_changes && !$targets_changed) {
1190                 print "\nMaking initiators aware of assignment changes..\n\n";
1191                 issueLIP();
1192         }
1193
1194         print "\n\nEncountered $errs error(s) while processing.\n" if ($errs);
1195
1196         if ($check) {
1197                 print "\nConfiguration checked, $changes difference(s) found with working configuration.\n";
1198         } else {
1199                 $changes = 0 if ($_DEBUG_);
1200                 print "\nConfiguration applied, $changes changes made.\n";
1201         }
1202
1203         return $TRUE if ($errs);
1204         return $FALSE;
1205 }
1206
1207 sub clearConfiguration {
1208         my $noprompt = shift;
1209         my $errs;
1210
1211         if (!$noprompt) {
1212                 print "WARNING: This removes ALL applied SCST configuration and may result in data loss!\n";
1213                 print "If this is not what you intend, press ctrl-c now. Waiting 10 seconds.\n\n";
1214                 sleep 10;
1215         }
1216
1217         print "\nRemoving all users and groups:\n\n";
1218         foreach my $group (keys %GROUPS) {
1219                 $errs += removeGroup($group) if ($group ne $_DEFAULT_GROUP_);
1220         }
1221
1222         print "\nRemoving all handler devices:\n\n";
1223         foreach my $device (keys %{$DEVICES}) {
1224                 next if (!$$DEVICES{$device});
1225                 next if ($SCST->handlerType($$DEVICES{$device}) != $SCST::SCST::IOTYPE_VIRTUAL);
1226                 $errs += removeDevice($_REVERSE_MAP_{$$DEVICES{$device}}, $device);
1227         }
1228
1229         print "\nEncountered $errs error(s) while processing.\n" if ($errs);
1230         print "\nConfiguration cleared.\n";
1231
1232         return $TRUE if ($errs);
1233         return $FALSE;
1234 }
1235
1236 sub showSessions {
1237         my $sessions = $SCST->sessions();
1238         immediateExit($SCST->errorString());
1239
1240         print "\n\tTarget Name\tInitiator Name\t\t\tGroup Name\t\tCommand Count\n";
1241
1242         foreach my $target (keys %{$sessions}) {
1243                 foreach my $group (keys %{$$sessions{$target}}) {
1244                         foreach my $user (keys %{$$sessions{$target}->{$group}}) {
1245                                 my $commands = $$sessions{$target}->{$group}->{$user};
1246
1247                                 print "\t$target\t$user\t\t$group\t\t$commands\n";
1248                         }
1249                 }
1250         }
1251
1252         print "\n";
1253
1254         return $FALSE;
1255 }
1256
1257 sub addDevice {
1258         my $handler = shift;
1259         my $device = shift;
1260         my $path = shift;
1261         my $options = shift;
1262         my $blocksize = shift;
1263
1264         my $_handler = $_HANDLER_MAP_{$handler};
1265         my $htype = $SCST->handlerType($_handler);
1266
1267         if (!$htype) {
1268                 print "WARNING: Internal error occured: ".$SCST->errorString()."\n";
1269                 return $TRUE;
1270         }
1271
1272         if ($htype != $SCST::SCST::IOTYPE_VIRTUAL) {
1273                 my $typeString = $_HANDLER_TYPE_MAP_{$htype};
1274                 my $validType = $_HANDLER_TYPE_MAP_{$SCST::SCST::IOTYPE_VIRTUAL};
1275                 print "WARNING: Handler $handler of type $typeString is incapable of ".
1276                   "opening/closing devices. Valid handlers are:\n".
1277                   validHandlerTypes($SCST::SCST::IOTYPE_VIRTUAL)."\n";
1278                 return $TRUE;
1279         }
1280
1281         if (defined($$DEVICES{$device})) {
1282                 print "WARNING: Device '$device' already defined.\n";
1283                 return $TRUE;
1284         }
1285
1286         print "\t-> Opening virtual device '$device' at path '$path' using handler '$handler'..\n";
1287
1288         if ($SCST->openDevice($_handler, $device, $path, $options, $blocksize)) {
1289                 print "WARNING: Failed to open virtual device '$device' at path '$path': ".
1290                   $SCST->errorString()."\n";
1291                 return $TRUE;
1292         }
1293
1294         $$DEVICES{$device} = $_handler;
1295
1296         return $FALSE;
1297 }
1298
1299 sub resyncDevice {
1300         my $handler = shift;
1301         my $device = shift;
1302
1303         my $_handler = $_HANDLER_MAP_{$handler};
1304         my $htype = $SCST->handlerType($_handler);
1305
1306         if (!defined($$DEVICES{$device})) {
1307                 print "WARNING: Device '$device' not defined.\n";
1308                 return $TRUE;
1309         }
1310
1311         print "\t-> Resync'ing virtual device '$device'..\n";
1312
1313         if ($SCST->resyncDevice($_handler, $device)) {
1314                 print "WARNING: Failed to resync virtual device '$device': ".
1315                   $SCST->errorString()."\n";
1316                 return $TRUE;
1317         }
1318
1319         return $FALSE;
1320 }
1321
1322 sub removeDevice {
1323         my $handler = shift;
1324         my $device = shift;
1325
1326         my $_handler = $_HANDLER_MAP_{$handler};
1327         my $htype = $SCST->handlerType($_handler);
1328
1329         if (!$htype) {
1330                 print "WARNING: Internal error occured: ".$SCST->errorString()."\n";
1331                 return $TRUE;
1332         }
1333
1334         if ($htype != $SCST::SCST::IOTYPE_VIRTUAL) {
1335                 my $typeString = $_HANDLER_TYPE_MAP_{$htype};
1336                 my $validType = $_HANDLER_TYPE_MAP_{$SCST::SCST::IOTYPE_VIRTUAL};
1337                 print "WARNING: Handler $handler of type $typeString is incapable of ".
1338                   "opening/closing devices. Valid handlers are:\n".
1339                   validHandlerTypes($SCST::SCST::IOTYPE_VIRTUAL)."\n";
1340                 return $TRUE;
1341         }
1342
1343         if (!defined($$DEVICES{$device})) {
1344                 print "WARNING: Device '$device' not defined.\n";
1345                 return $TRUE;
1346         }
1347
1348         print "\t-> Closing virtual device '$device'..\n";
1349
1350         if ($SCST->closeDevice($_handler, $device)) {
1351                 print "WARNING: Failed to close virtual device '$device': ".
1352                   $SCST->errorString()."\n";
1353                 return $TRUE;
1354         }
1355
1356         undef $$DEVICES{$device};
1357
1358         return $FALSE;
1359 }
1360
1361 sub setT10DeviceId {
1362         my $handler = shift;
1363         my $device = shift;
1364         my $t10_id = shift;
1365
1366         my $_handler = $_HANDLER_MAP_{$handler};
1367         my $htype = $SCST->handlerType($_handler);
1368
1369         if (!defined($$DEVICES{$device})) {
1370                 print "WARNING: Device '$device' not defined.\n";
1371                 return $TRUE;
1372         }
1373
1374         print "\t-> Changing T10 Device ID of virtual device '$device' to '$t10_id'..\n";
1375
1376         if ($SCST->setT10DeviceId($_handler, $device, $t10_id)) {
1377                 print "WARNING: Failed to changing T10 Device ID of virtual device '$device': ".
1378                   $SCST->errorString()."\n";
1379                 return $TRUE;
1380         }
1381
1382         return $FALSE;
1383 }
1384
1385 sub addGroup {
1386         my $group = shift;
1387
1388         if (defined($GROUPS{$group})) {
1389                 print "WARNING: Group '$group' already exists.\n";
1390                 return $TRUE;
1391         }
1392
1393         print "\t-> Creating security group '$group'..\n";
1394
1395         if ($SCST->addGroup($group)) {
1396                 print "WARNING: Failed to create security group '$group': ".
1397                   $SCST->errorString()."\n";
1398                 return $TRUE;
1399         }
1400
1401         $GROUPS{$group}++;
1402
1403         return $FALSE;
1404 }
1405
1406 sub renameGroup {
1407         my $group = shift;
1408         my $toGroup = shift;
1409
1410         if (defined($GROUPS{$toGroup})) {
1411                 print "WARNING: Group '$toGroup' already exists.\n";
1412                 return $TRUE;
1413         }
1414
1415         print "\t-> Renaming security group '$group' to '$toGroup'..\n";
1416
1417         if ($SCST->renameGroup($group, $toGroup)) {
1418                 print "WARNING: Failed to rename security group '$group' to ".
1419                   "'$toGroup': ".$SCST->errorString()."\n";
1420                 return $TRUE;
1421         }
1422
1423         delete $GROUPS{$group};
1424         $GROUPS{$toGroup}++;
1425
1426         return $FALSE;
1427 }
1428
1429 sub removeGroup {
1430         my $group = shift;
1431
1432         return $FALSE if ($group eq $_DEFAULT_GROUP_);
1433
1434         if (!defined($GROUPS{$group})) {
1435                 print "WARNING: Group '$group' does not exist.\n";
1436                 return $TRUE;
1437         }
1438
1439         print "\t-> Removing security group '$group'..\n";
1440
1441         if ($SCST->removeGroup($group)) {
1442                 print "WARNING: Failed to remove security group '$group': ".
1443                   $SCST->errorString()."\n";
1444                 return $TRUE;
1445         }
1446
1447         undef $GROUPS{$group};
1448
1449         return $FALSE;
1450 }
1451
1452 sub addUser {
1453         my $group = shift;
1454         my $user = shift;
1455
1456         if (!defined($GROUPS{$group})) {
1457                 print "WARNING: Failed to add user '$user' to group '$group', group does not exist.\n";
1458                 return $TRUE;
1459         }
1460
1461         if (defined($USERS{$group}->{$user})) {
1462                 print "WARNING: User '$user' already exists in security group '$group'.\n";
1463                 return $TRUE;
1464         }
1465
1466         print "\t-> Adding user '$user' to security group '$group'..\n";
1467
1468         if ($SCST->addUser($user, $group)) {
1469                 print "WARNING: Failed to add user '$user' to security group '$group': ".
1470                   $SCST->errorString()."\n";
1471                 return $TRUE;
1472         }
1473
1474         $USERS{$group}->{$user}++;
1475
1476         return $FALSE;
1477 }
1478
1479 sub moveUser {
1480         my $group = shift;
1481         my $user = shift;
1482         my $toGroup = shift;
1483
1484         if (!defined($GROUPS{$group})) {
1485                 print "WARNING: Failed to move user '$user' from group '$group', group does not exist.\n";
1486                 return $TRUE;
1487         }
1488
1489         if (defined($USERS{$toGroup}->{$user})) {
1490                 print "WARNING: User '$user' already exists in security group '$toGroup'.\n";
1491                 return $TRUE;
1492         }
1493
1494         print "\t-> Moving user '$user' from security group '$group' to security group '$toGroup'..\n";
1495
1496         if ($SCST->moveUser($user, $group, $toGroup)) {
1497                 print "WARNING: Failed to move user '$user' from security group '$group' to ".
1498                   "security group '$toGroup': ".$SCST->errorString()."\n";
1499                 return $TRUE;
1500         }
1501
1502         delete $USERS{$group}->{$user};
1503         $USERS{$toGroup}->{$user}++;
1504
1505         return $FALSE;
1506 }
1507
1508 sub removeUser {
1509         my $group = shift;
1510         my $user = shift;
1511
1512         if (!defined($GROUPS{$group})) {
1513                 print "WARNING: Failed to remove user '$user' from group '$group', group does not exist.\n";
1514                 return $TRUE;
1515         }
1516
1517         if (!defined($USERS{$group}->{$user})) {
1518                 print "WARNING: User '$user' doesn\'t exist in security group '$group'.\n";
1519                 return $TRUE;
1520         }
1521
1522         print "\t-> Removing user '$user' from security group '$group'..\n";
1523
1524         if ($SCST->removeUser($user, $group)) {
1525                 print "WARNING: Failed to remove user '$user' to security group '$group': ".
1526                   $SCST->errorString()."\n";
1527                 return $TRUE;
1528         }
1529
1530         undef $USERS{$group}->{$user};
1531
1532         return $FALSE;
1533 }
1534
1535 sub clearUsers {
1536         my $group = shift;
1537
1538         if (!defined($GROUPS{$group})) {
1539                 print "WARNING: Failed to clear users from group '$group', group does not exist.\n";
1540                 return $TRUE;
1541         }
1542
1543         print "\t-> Clearing users from security group '$group'..\n";
1544
1545         if ($SCST->clearUsers($group)) {
1546                 print "WARNING: Failed to clear users from security group '$group': ".
1547                   $SCST->errorString()."\n";
1548                 return $TRUE;
1549         }
1550
1551         undef $USERS{$group};
1552
1553         return $FALSE;
1554 }
1555
1556 sub assignDevice {
1557         my $group = shift;
1558         my $device = shift;
1559         my $lun = shift;
1560         my $options = shift;
1561         my %allLuns;
1562
1563         # Put luns into something easier to parse..
1564         foreach my $_group (keys %ASSIGNMENTS) {
1565                 my $_gAssigns = $ASSIGNMENTS{$_group};
1566
1567                 foreach my $_device (keys %{$_gAssigns}) {
1568                         @{$allLuns{$_group}}[$$_gAssigns{$_device}] = $_device;
1569                 }
1570         }
1571
1572         # Use the next available LUN if none specified
1573         if ($lun !~ /\d+/) {
1574                 $lun = ($#{$allLuns{$group}} + 1);
1575                 if ($lun > $_MAX_LUNS_) {
1576                         print "ERROR: Unable to use next available LUN of $lun, lun out of range.\n";
1577                         return $TRUE;
1578                 }
1579
1580                 print "\t-> Device '$device': Using next available LUN of $lun for group '$group'.\n";
1581         }
1582                         
1583         if (($lun < 0) || ($lun > $_MAX_LUNS_)) {
1584                 print "ERROR: Unable to assign device '$device', lun '$lun' is out of range.\n";
1585                 return $TRUE;
1586         }
1587
1588         if (!defined($$DEVICES{$device})) {
1589                 print "WARNING: Unable to assign non-existant device '$device' to group '$group'.\n";
1590                 return $TRUE;
1591         }
1592
1593         if (@{$allLuns{$group}}[$lun]) {
1594                 print "ERROR: Device '$device': Lun '$lun' is already assigned to device '".@{$allLuns{$group}}[$lun]."'.\n";
1595                 return $TRUE;
1596         }
1597
1598         print "\t-> Assign virtual device '$device' to group '$group' at LUN '$lun'..\n";
1599
1600         if ($SCST->assignDeviceToGroup($device, $group, $lun, $options)) {
1601                 print "WARNING: Failed to assign device '$device' to group '$group': ".
1602                   $SCST->errorString()."\n";
1603                 return $TRUE;
1604         }
1605
1606         if (!defined($ASSIGNMENTS{$group})) {
1607                 my %assignments_t;
1608                 $ASSIGNMENTS{$group} = \%assignments_t;
1609         }
1610
1611         my $_assignments = $ASSIGNMENTS{$group};
1612
1613         $$_assignments{$device} = $lun; 
1614
1615         return $FALSE;
1616 }
1617
1618 sub replaceDevice {
1619         my $group = shift;
1620         my $newDevice = shift;
1621         my $lun = shift;
1622         my $options = shift;
1623         my %allLuns;
1624
1625         # Put luns into something easier to parse..
1626         foreach my $_group (keys %ASSIGNMENTS) {
1627                 my $_gAssigns = $ASSIGNMENTS{$_group};
1628
1629                 foreach my $_device (keys %{$_gAssigns}) {
1630                         @{$allLuns{$_group}}[$$_gAssigns{$_device}] = $_device;
1631                 }
1632         }
1633                         
1634         if (!defined($$DEVICES{$newDevice})) {
1635                 print "WARNING: Unable to assign non-existant device '$newDevice' to group '$group'.\n";
1636                 return $TRUE;
1637         }
1638
1639         if (${$allLuns{$group}}[$lun] eq $newDevice) {
1640                 print "ERROR: Device '$newDevice': Lun '$lun' is already assigned to device '$newDevice'.\n";
1641                 return $TRUE;
1642         }
1643
1644         print "\t-> Replace device at LUN '$lun' in group '$group' with new device '$newDevice'..\n";
1645
1646         if ($SCST->replaceDeviceInGroup($newDevice, $group, $lun, $options)) {
1647                 print "WARNING: Failed to replace LUN '$lun' in group '$group' with new device '$newDevice': ".
1648                   $SCST->errorString()."\n";
1649                 return $TRUE;
1650         }
1651
1652         if (!defined($ASSIGNMENTS{$group})) {
1653                 my %assignments_t;
1654                 $ASSIGNMENTS{$group} = \%assignments_t;
1655         }
1656
1657         my $_assignments = $ASSIGNMENTS{$group};
1658
1659         delete $$_assignments{${$allLuns{$group}}[$lun]};
1660         $$_assignments{$newDevice} = $lun;
1661
1662         return $FALSE;
1663 }
1664
1665 sub releaseDevice {
1666         my $group = shift;
1667         my $device = shift;
1668
1669         if (!defined($GROUPS{$group})) {
1670                 print "WARNING: Failed to release device '$device' from group '$group', group does not exist.\n";
1671                 return $TRUE;
1672         }
1673
1674         if (!defined($$DEVICES{$device})) {
1675                 print "WARNING: Failed to release device '$device', device not defined.\n";
1676                 return $TRUE;
1677         }
1678
1679         print "\t-> Release virtual device '$device' from group '$group'..\n";
1680
1681         if ($SCST->removeDeviceFromGroup($device, $group)) {
1682                 print "WARNING: Failed to release device '$device' from group '$group': ".
1683                   $SCST->errorString()."\n";
1684                 return $TRUE;
1685         }
1686
1687         my $_assignments = $ASSIGNMENTS{$group};
1688
1689         undef $$_assignments{$device};
1690
1691         return $FALSE;
1692 }
1693
1694 sub clearDevices {
1695         my $group = shift;
1696
1697         if (!defined($GROUPS{$group})) {
1698                 print "WARNING: Failed to clear devices from group '$group', group does not exist.\n";
1699                 return $TRUE;
1700         }
1701
1702         print "\t-> Clear virtual devices from group '$group'..\n";
1703
1704         if ($SCST->clearGroupDevices($group)) {
1705                 print "WARNING: Failed to clear devices from group '$group': ".
1706                   $SCST->errorString()."\n";
1707                 return $TRUE;
1708         }
1709
1710         undef $ASSIGNMENTS{$group};
1711
1712         return $FALSE;
1713 }
1714
1715 sub targets {
1716         my %targets;
1717         my %fcards;
1718
1719         my $root = new IO::Dir $_FC_CLASS_ if (-d $_FC_CLASS_);
1720
1721         if ($root) {
1722                 while (my $entry = $root->read()) {
1723                         next if (($entry eq '.') || ($entry eq '..'));
1724
1725                         my $io = new IO::File "$_FC_CLASS_/$entry/port_name", O_RDONLY;
1726
1727                         if ($io) {
1728                                 my $wwn = <$io>;
1729                                 chomp $wwn;
1730                                 close $io;
1731
1732                                 $fcards{$entry} = $wwn;
1733                         }
1734                 }
1735         }
1736
1737         $root = new IO::Dir $_SCSI_CLASS_ if (-d $_SCSI_CLASS_);
1738
1739         if ($root) {
1740                 while (my $entry = $root->read()) {
1741                         next if (($entry eq '.') || ($entry eq '..'));
1742
1743                         my $io = new IO::File "$_SCSI_CLASS_/$entry/target_mode_enabled", O_RDONLY;
1744
1745                         if ($io) {
1746                                 my $enabled = <$io>;
1747                                 chomp $enabled;
1748                                 close $io;
1749
1750                                 $targets{$entry}->{'path'} = "$_SCSI_CLASS_/$entry/target_mode_enabled";
1751                                 $targets{$entry}->{'enabled'} = $enabled;
1752                                 $targets{$entry}->{'qla_isp'} = $FALSE;
1753
1754                                 if ($fcards{$entry}) {
1755                                         $targets{$fcards{$entry}}->{'enabled'} = $enabled;
1756                                         $targets{$fcards{$entry}}->{'path'} =
1757                                           "$_SCSI_CLASS_/$entry/target_mode_enabled";
1758
1759                                         if (-f "$_FC_CLASS_/$entry/issue_lip") {
1760                                                 $targets{$fcards{$entry}}->{'ilip'} = "$_FC_CLASS_/$entry/issue_lip";
1761                                         }
1762
1763                                         $targets{$entry}->{'duplicate'} = $TRUE;
1764                                 } else {
1765                                         $targets{$entry}->{'duplicate'} = $FALSE;
1766                                 }
1767                         }
1768                 }
1769         }
1770
1771         $root = new IO::Dir $_SCSI_ISP_ if (-d $_SCSI_ISP_);
1772
1773         if ($root) {
1774                 while (my $entry = $root->read()) {
1775                         next if (($entry eq '.') || ($entry eq '..'));
1776
1777                         local $/;
1778                         my $io = new IO::File "$_SCSI_ISP_/$entry", O_RDONLY;
1779
1780                         if ($io) {
1781                                 my $wwn;
1782                                 my $fstr;
1783                                 my $enabled2;
1784
1785                                 $fstr = <$io>;
1786                                 close $io;
1787
1788                                 ($wwn) = ($fstr =~ '.*?Port WWN +([^\ ]+) .*');
1789                                 $fcards{$entry} = $wwn;
1790
1791                                 $io = new IO::File "$_SCSITGT_QLAISP_/$entry", O_RDONLY;
1792                                 if ($io) {
1793                                         $fstr = <$io>;
1794                                         close $io;
1795
1796                                         ($enabled2) = ($fstr =~ '[^\n]+\n *\d *: *(\d)');
1797                                         $targets{$entry}->{'path'} = "$_SCSITGT_QLAISP_/$entry";
1798                                         $targets{$entry}->{'enabled'} = $enabled2;
1799                                         $targets{$entry}->{'qla_isp'} = $TRUE;
1800
1801                                         if ($fcards{$entry}) {
1802                                                 $targets{$fcards{$entry}}->{'enabled'} = $enabled2;
1803                                                 $targets{$fcards{$entry}}->{'path'} = "$_SCSITGT_QLAISP_/$entry";
1804                                                 $targets{$fcards{$entry}}->{'qla_isp'} = $TRUE;
1805                                                 $targets{$entry}->{'duplicate'} = $TRUE;
1806                                         } else {
1807                                                 $targets{$entry}->{'duplicate'} = $FALSE;
1808                                         }
1809                                 }
1810                         }
1811                 }
1812         }
1813
1814         return \%targets;
1815 }
1816
1817 sub targetEnabled {
1818         my $target = shift;
1819
1820         return undef if (!defined($$TARGETS{$target}));
1821         return $$TARGETS{$target}->{'enabled'};
1822 }
1823
1824 sub enableTarget {
1825         my $target = shift;
1826         my $enable = shift;
1827
1828         $target = unformatTarget($target);
1829
1830         return undef if (!defined($$TARGETS{$target}));
1831
1832         my $io = new IO::File $$TARGETS{$target}->{'path'}, O_WRONLY;
1833         return $TRUE if (!$io);
1834
1835         print $enable ? "\t-> Enabling" : "\t-> Disabling";
1836         print " target mode for SCST host '$target'.\n";
1837
1838         if ($_DEBUG_) {
1839                 print "DBG($$): ".$$TARGETS{$target}->{'path'}." -> $enable\n\n";
1840         } else {
1841                 if ($$TARGETS{$target}->{'qla_isp'} == $FALSE) {
1842                         print $io $enable;
1843                 } else {
1844                         print $io $enable ? "enable all" : "disable all";
1845                 }
1846         }
1847
1848         close $io;
1849
1850         $$TARGETS{$target}->{'enabled'} = $enable;
1851
1852         return $FALSE;
1853 }
1854
1855 sub issueLIP {
1856         my $lip_issued;
1857
1858         foreach my $target (keys %{$TARGETS}) {
1859                 next if ($$TARGETS{$target}->{'duplicate'});
1860                 next if (!$$TARGETS{$target}->{'enabled'});
1861
1862                 if (defined($$TARGETS{$target}->{'ilip'})) {
1863                         my $io = new IO::File $$TARGETS{$target}->{'ilip'}, O_WRONLY;
1864                         return $TRUE if (!$io);
1865
1866                         print "\t-> Issuing LIP for target '".formatTarget($target)."': ";
1867
1868                         if ($_DEBUG_) {
1869                                 print "DBG($$): ".$$TARGETS{$target}->{'ilip'}." -> $TRUE\n\n";
1870                         } else {
1871                                 print $io $TRUE;
1872                         }
1873
1874                         print "done.\n";
1875
1876                         $lip_issued = $TRUE;
1877                 }
1878         }
1879
1880         print "\t-> WARNING: This target driver does not support issuing LIPs.\n\n"
1881           if (!$lip_issued);
1882 }
1883
1884 sub readConfig {
1885         my $confile = shift;
1886         my $ignoreError = shift;
1887         my %config;
1888         my $section;
1889         my $last_section;
1890         my $arg;
1891         my $last_arg;
1892         my %empty;
1893
1894         my $io = new IO::File $confile, O_RDONLY;
1895
1896         if (!$io) {
1897                 return undef if ($ignoreError);
1898
1899                 die("FATAL: Unable to open specified configuration file $confile: $!\n");
1900         }
1901
1902         while (my $line = <$io>) {
1903                 ($line, undef) = split(/\#/, $line, 2);
1904                 $line = cleanupString($line);
1905
1906                 if ($line =~ /^\[(.*)\]$/) {
1907                         ($section, $arg) = split(/\s+/, $1, 2);
1908
1909                         $arg = 'default' if ($section eq 'OPTIONS');
1910
1911                         if ($last_arg && ($last_arg ne $arg) &&
1912                             !defined($config{$last_section}->{$last_arg})) {
1913                                 $config{$last_section}->{$last_arg} = \%empty;
1914                         }   
1915
1916                         $last_arg = $arg;
1917                         $last_section = $section;
1918                 } elsif ($section && $arg && $line) {
1919                         my($parameter, $value) = split(/\s+/, $line, 2);
1920
1921                         if ($section eq 'OPTIONS') {
1922                                 $value = $TRUE if (($value == 1) ||
1923                                                    ($value =~ /^TRUE$/i) ||
1924                                                    ($value =~ /^YES$/i));
1925                                 $value = $FALSE if (($value == 0) ||
1926                                                     ($value =~ /^FALSE$/i) ||
1927                                                     ($value =~ /^NO$/i));
1928                         }
1929
1930                         push @{$config{$section}->{$arg}->{$parameter}}, $value;
1931                 }
1932         }
1933
1934         close $io;      
1935
1936         return \%config;
1937 }
1938
1939 sub findUserGroup {
1940         my $user = shift;
1941         my $config = shift;
1942
1943         foreach my $group (keys %{$$config{'GROUP'}}) {
1944                 foreach my $_user (@{$$config{'GROUP'}->{$group}->{'USER'}}) {
1945                         return $group if ($_user eq $user);
1946                 }
1947         }
1948
1949         return undef;
1950 }
1951
1952 sub findUserGroupInCurrent {
1953         my $user = shift;
1954
1955         foreach my $group (keys %USERS) {
1956                 foreach my $_user (keys %{$USERS{$group}}) {
1957                         return $group if ($_user eq $user);
1958                 }
1959         }
1960
1961         return undef;
1962 }
1963
1964 sub findAssignedLun {
1965         my $associations = shift;
1966         my $lun = shift;
1967
1968         return undef if (!defined($lun));
1969
1970         foreach my $device (keys %{$associations}) {
1971                 if ($$associations{$device} == $lun) {
1972                         return $device;
1973                 }
1974         }
1975
1976         return undef;
1977 }
1978
1979 sub cleanupString {
1980         my $string = shift;
1981
1982         $string =~ s/^\s+//;
1983         $string =~ s/\s+$//;
1984
1985         return $string;
1986 }
1987
1988 sub formatTarget {
1989         my $target = shift;
1990
1991         if ($target =~ /^0x/) {
1992                 $target =~ s/^0x//;
1993                 my($o1, $o2, $o3, $o4, $o5, $o6, $o7, $o8) = unpack("A2A2A2A2A2A2A2A2", $target);
1994                 $target = "$o1:$o2:$o3:$o4:$o5:$o6:$o7:$o8";
1995         }
1996
1997         $target =~ tr/A-Z/a-z/;
1998
1999         return $target;
2000 }
2001
2002 sub unformatTarget {
2003         my $target = shift;
2004
2005         if ($target =~ /^.{2}\:.{2}\:.{2}\:.{2}\:.{2}\:.{2}\:.{2}\:.{2}/) {
2006                 $target =~ s/\://g;
2007                 $target = "0x$target";
2008         }
2009
2010         $target =~ tr/A-Z/a-z/;
2011
2012         return $target;
2013 }
2014
2015 sub validHandlerTypes {
2016         my $type = shift;
2017         my $buffer = "\n";
2018
2019         foreach my $handler (keys %_REVERSE_MAP_) {
2020                 $buffer .= "\t".$_REVERSE_MAP_{$handler}."\n" if ($SCST->handlerType($handler) == $type);
2021         }
2022
2023         return $buffer;
2024 }
2025
2026 sub randomGroup {
2027         my $tmp_group;
2028         my $retry = 0;
2029
2030         while (($retry < 10000) && (!$tmp_group || $SCST->groupExists($tmp_group))) {
2031                 my $id = int(rand(10000));
2032
2033                 $tmp_group = $_TGT_TMP_PREFIX_.$id;
2034
2035                 $retry++;
2036         }
2037
2038         return undef if ($SCST->groupExists($tmp_group));
2039         return $tmp_group;
2040 }
2041
2042 # If we have an unread error from SCST, exit immediately
2043 sub immediateExit {
2044         my $error = shift;
2045
2046         return if (!$error);
2047
2048         print "\n\nFATAL: Received the following error:\n\n\t";
2049         print "$error\n\n";
2050
2051         exit 1;
2052 }
2053
2054 # Hey! Stop that!
2055 sub commitSuicide {
2056         print "\n\nAborting immediately.\n";
2057         exit 1;
2058 }