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