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