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