- Fixed applyConfiguration() to remove entries before adding new ones.
[mirror/scst/.git] / scstadmin / scstadmin
1 #!/usr/bin/perl
2 $Version  = 'SCST Configurator v0.6.1';
3
4 # Configures SCST
5 #
6 # Written by Mark R. Buechler 12/07/04
7
8 sub usage
9   {
10     die <<"EndUsage";
11 $Version
12
13 Usage:
14 General Operations
15      -config <config>     : Configure SCST given the specified configuration file.
16      -ClearConfig         : Clear all SCST configuration.
17      -WriteConfig <file>  : Writes the current configuration out to the specified file.
18      -checkConfig <file>  : Checks the saved configuration in the specified file.
19      -sessions            : List current initiator sessions.
20      
21 Target Driver Operations
22      -enable <wwn|host>   : Enable target mode for driver at specified WWN or host.
23      -disable <wwn|host>  : Disable target mode for driver at specified WWN or host.
24
25 Devices Operations
26      -adddev <device>     : Adds a device to a handler.
27          -handler <handler>
28          -path <path>
29          -options <options>
30          -blocksize <bytes>
31      -RemoveDev <device>  : Remove a device from a handler.
32          -handler <handler>
33  
34 Users Operations
35      -adduser <user>      : Adds a user to a security group.
36          -group <group>
37      -RemoveUser <user>   : Delete a user from a security group.
38          -group <group>
39      -ClearUsers          : Clear all users from a given security group.
40          -group <group>
41
42 Groups Operations
43      -addgroup <group>    : Add a given group to available security groups.
44      -RemoveGroup <group> : Remove a give group from available security groups.
45      
46 Assignments Operations
47      -assigndev <device>  : Assign a given device to a security group.
48          -group <group>
49          -lun <lun>
50      -ReleaseDev <device> : Remove a given device from a security group.
51          -group <group>
52      -ClearDevs           : Clear all device assignments for a security group.
53          -group <group>
54
55 Options
56      -ForceConfig         : Force all confuration changes, even deletions (DANGER!).
57   
58 Debugging (limited support)
59      -debug               : Debug mode - don\'t do anything destructive.
60
61 Available Handlers:
62       disk, vdisk, disk_perf, cdrom, vcdrom, changer, modisk, modisk_perf, tape, tape_perf
63
64 Available Options for create and open:
65       WRITE_THROUGH, READ_ONLY, O_DIRECT, NULLIO, NV_CACHE, BLOCKIO
66      
67 Examples:
68      Enable target mode for fibre card specifying its WWN
69        scstadmin -enable 50:06:0B:00:00:39:71:78
70
71      Disable target mode for SCSI host specifying host number
72        scstadmin -disable host4 
73
74      Create a new security group:
75        scstadmin -addgroup HOST01
76        
77      Create a device given an already existing disk file:
78        scstadmin -adddev DISK01 -handler vdisk -path /vdisks/disk01.dsk -options READ_ONLY,WRITE_THROUGH
79        
80      Assign a device to a security group:
81        scstadmin -assigndev DISK01 -group HOST01 -lun 1
82
83 EndUsage
84   }
85
86 use SCST::SCST;
87 use Getopt::Long;
88 use IO::File;
89 use IO::Dir;
90 use POSIX;
91 use strict;
92
93 my $_DEF_CONFIG_ = '/etc/scst.conf';
94
95 my $TRUE  = 1;
96 my $FALSE = 0;
97
98 my $_MAX_LUNS_      = 255;
99 my $_DEFAULT_GROUP_ = 'Default';
100
101 my $_SCSI_CLASS_    = '/sys/class/scsi_host';
102 my $_FC_CLASS_      = '/sys/class/fc_host';
103
104 my $SCST;
105 my $DEVICES;
106 my $TARGETS;
107 my %USERS;
108 my %ASSIGNMENTS;
109 my %HANDLERS;
110 my %GROUPS;
111 my $_DEBUG_;
112
113 my %_HANDLER_MAP_ = ('cdrom' => $SCST::SCST::CDROM_TYPE,
114                      'changer' => $SCST::SCST::CHANGER_TYPE,
115                      'disk' => $SCST::SCST::DISK_TYPE,
116                      'vdisk' => $SCST::SCST::VDISK_TYPE,
117                      'vcdrom' => $SCST::SCST::VCDROM_TYPE,
118                      'disk_perf' => $SCST::SCST::DISKPERF_TYPE,
119                      'modisk' => $SCST::SCST::MODISK_TYPE,
120                      'modisk_perf' => $SCST::SCST::MODISKPERF_TYPE,
121                      'tape' => $SCST::SCST::TAPE_TYPE,
122                      'tape_perf' => $SCST::SCST::TAPEPERF_TYPE,
123                      'processor' => $SCST::SCST::PROCESSOR_TYPE,
124                 # Add in the dev_ names as well
125                      'dev_cdrom' => $SCST::SCST::CDROM_TYPE,
126                      'dev_changer' => $SCST::SCST::CHANGER_TYPE,
127                      'dev_disk' => $SCST::SCST::DISK_TYPE,
128                      'dev_disk_perf' => $SCST::SCST::DISKPERF_TYPE,
129                      'dev_modisk' => $SCST::SCST::MODISK_TYPE,
130                      'dev_modisk_perf' => $SCST::SCST::MODISKPERF_TYPE,
131                      'dev_tape' => $SCST::SCST::TAPE_TYPE,
132                      'dev_tape_perf' => $SCST::SCST::TAPEPERF_TYPE,
133                      'dev_processor' => $SCST::SCST::PROCESSOR_TYPE);
134                      
135 my %_REVERSE_MAP_ = ($SCST::SCST::CDROM_TYPE => 'cdrom',
136                      $SCST::SCST::CHANGER_TYPE => 'changer',
137                      $SCST::SCST::DISK_TYPE => 'disk',
138                      $SCST::SCST::VDISK_TYPE => 'vdisk',
139                      $SCST::SCST::VCDROM_TYPE => 'vcdrom',
140                      $SCST::SCST::DISKPERF_TYPE => 'disk_perf',
141                      $SCST::SCST::MODISK_TYPE => 'modisk',
142                      $SCST::SCST::MODISKPERF_TYPE => 'modisk_perf',
143                      $SCST::SCST::TAPE_TYPE => 'tape',
144                      $SCST::SCST::TAPEPERF_TYPE => 'tape_perf',
145                      $SCST::SCST::PROCESSOR_TYPE => 'processor');
146
147 my %_HANDLER_TYPE_MAP_ = ($SCST::SCST::IOTYPE_PHYSICAL => 'physical',
148                           $SCST::SCST::IOTYPE_VIRTUAL => 'virtual',
149                           $SCST::SCST::IOTYPE_PERFORMANCE => 'performance');
150
151 $SIG{INT} = \&commitSuicide;
152
153 use vars qw($Version);
154
155 POSIX::setsid();
156
157 &main();
158
159 sub getArgs {
160         my $applyConfig;
161         my $forceConfig;
162         my $clearConfig;
163         my $writeConfig;
164         my $checkConfig;
165         my $showSessions;
166         my $addDev;
167         my $devPath;
168         my $removeDev;
169         my $addUser;
170         my $removeUser;
171         my $clearUsers;
172         my $addGroup;
173         my $removeGroup;
174         my $assignDev;
175         my $releaseDev;
176         my $clearDevs;
177         my $devLun;
178         my $handler;
179         my $group;
180         my $options;
181         my $blocksize;
182         my $enable;
183         my $disable;
184
185         my $p = new Getopt::Long::Parser;
186
187         if (!$p->getoptions('config:s'          => \$applyConfig,
188                             'ClearConfig'       => \$clearConfig,
189                             'ForceConfig'       => \$forceConfig,
190                             'WriteConfig=s'     => \$writeConfig,
191                             'checkConfig=s'     => \$checkConfig,
192                             'sessions'          => \$showSessions,
193                             'adddev=s'          => \$addDev,
194                             'path=s'            => \$devPath,
195                             'RemoveDev=s'       => \$removeDev,
196                             'lun=s'             => \$devLun,
197                             'adduser=s'         => \$addUser,
198                             'RemoveUser=s'      => \$removeUser,
199                             'ClearUsers'        => \$clearUsers,
200                             'addgroup=s'        => \$addGroup,
201                             'RemoveGroup=s'     => \$removeGroup,
202                             'assigndev=s'       => \$assignDev,
203                             'ReleaseDev=s'      => \$releaseDev,
204                             'ClearDevs'         => \$clearDevs,
205                             'handler=s'         => \$handler,
206                             'group=s'           => \$group,
207                             'options=s'         => \$options,
208                             'blocksize=s'       => \$blocksize,
209                             'enable=s'          => \$enable,
210                             'disable=s'         => \$disable,
211                             'debug'             => \$_DEBUG_)) {
212                 &usage();
213         }
214
215         if ((defined($enable) && !$enable) || (defined($disable) && !$disable)) {
216                 print "Argument -enable/-disable requires a WWN or host.\n\n";
217                 usage();
218         }
219
220         if ($handler && !$_HANDLER_MAP_{$handler}) {
221                 print "Invalid handler '$handler' specified. Available handlers are:\n\n";
222                 foreach my $_handler (keys %_HANDLER_MAP_) {
223                         print "\t$_handler\n";
224                 }
225                 print "\n";
226                 exit 1;
227         }
228
229         if ($addDev && !($handler && $devPath)) {
230                 print "Please specify -handler and -path with -adddev.\n\n";
231                 usage();
232         }
233
234         if (defined($blocksize) && !$blocksize) {
235                 print "Please specify bytes with -blocksize.\n\n";
236                 usage();
237         }
238
239         if ($blocksize && !$addDev) {
240                 print "Please specify -adddev with -blocksize.\n";
241                 usage();
242         }
243
244         if (defined($forceConfig) && !defined($applyConfig)) {
245                 print "Please specify -config with -ForceConfig.\n\n";
246                 usage();
247         }
248
249         if ($removeDev && !$handler) {
250                 print "Please specify -handler with -RemoveDev.\n\n";
251                 usage();
252         }
253
254         if ($addUser && !defined($group)) {
255                 print "Please specify -group with -adduser.\n\n";
256                 usage();
257         }
258
259         if ($removeUser && !defined($group)) {
260                 print "Please specify -group with -RemoveUser.\n\n";
261                 usage();
262         }
263
264         if ($clearUsers && !defined($group)) {
265                 print "Please specify -group with -ClearUsers.\n\n";
266                 usage();
267         }
268
269         if ($assignDev && !(defined($group) && defined($devLun))) {
270                 print "Please specify -group and -lun with -assigndev.\n\n";
271                 usage();
272         }
273
274         if ($releaseDev && !defined($group)) {
275                 print "Please specify -group with -ReleaseDev.\n\n";
276                 usage();
277         }
278
279         if ($clearDevs && !defined($group)) {
280                 print "Please specify -group with -ClearDevs.\n\n";
281                 usage();
282         }
283
284         if (defined($writeConfig) && !$writeConfig) {
285                 print "Please specify a file name to write configuration to..\n\n";
286                 usage();
287         }
288
289         $_DEBUG_ = $TRUE if (defined($_DEBUG_));
290
291         $forceConfig = $TRUE if (defined($forceConfig));
292         $showSessions = $TRUE if (defined($showSessions));
293
294         $enable =~ tr/A-Z/a-z/; $disable =~ tr/A-Z/a-z/;
295
296         if ((defined($showSessions) + defined($addDev) + defined($removeDev) +
297              defined($addUser) + defined($enable) + defined($disable) +
298              defined($removeUser) + defined($clearUsers) + defined($assignDev) +
299              defined($releaseDev) + defined($clearDevs) + defined($applyConfig) +
300              defined($clearConfig) + defined($writeConfig) + defined($checkConfig)) > 1) {
301                 print "Please specify only one operation at a time.\n";
302                 usage();
303         }
304
305         $applyConfig = $_DEF_CONFIG_ if (defined($applyConfig) && !$applyConfig);
306         $checkConfig = $_DEF_CONFIG_ if (defined($checkConfig) && !$checkConfig);
307
308         return ($enable, $disable, $addDev, $devPath, $devLun, $removeDev, $addUser, $removeUser,
309                 $clearUsers, $addGroup, $removeGroup, $assignDev, $releaseDev, $clearDevs,
310                 $handler, $group, $options, $blocksize, $applyConfig, $forceConfig,
311                 $clearConfig, $writeConfig, $checkConfig, $showSessions);
312 }
313
314 sub main {
315         my $rc;
316
317         STDOUT->autoflush(1);
318
319         # We need to run as root
320         if ( $> ) {die("This program must run as root.\n");}
321
322         my ($enable, $disable, $addDev, $devPath, $devLun, $removeDev, $addUser, $removeUser,
323             $clearUsers, $addGroup, $removeGroup, $assignDev, $releaseDev, $clearDevs,
324             $handler, $group, $options, $blocksize, $applyConfig, $forceConfig,
325             $clearConfig, $writeConfig, $checkConfig, $showSessions) = getArgs();
326
327         $SCST = new SCST::SCST($_DEBUG_);
328
329         readCurrentConfig();
330
331         SWITCH: {
332                 $applyConfig && do {
333                         if ($forceConfig) {
334                                 $rc = applyConfiguration($applyConfig, $FALSE, $TRUE);
335                                 die("Configuration errors found, aborting.\n") if ($rc);
336
337                                 print "\nConfiguration will apply in 10 seconds, type ctrl-c to abort..\n";
338                                 sleep 10;
339                         }
340
341                         $rc = applyConfiguration($applyConfig, $forceConfig, $FALSE);
342                         last SWITCH;
343                 };
344                 $checkConfig && do {
345                         $rc = applyConfiguration($checkConfig, $FALSE, $TRUE);
346                         last SWITCH;
347                 };
348                 $writeConfig && do {
349                         $rc = writeConfiguration($writeConfig);
350                         last SWITCH;
351                 };
352                 $showSessions && do {
353                         $rc = showSessions();
354                         last SWITCH;
355                 };
356                 defined($clearConfig) && do {
357                         $rc = clearConfiguration();
358                         last SWITCH;
359                 };
360                 $addDev && do {
361                         $rc = addDevice($handler, $addDev, $devPath, $options, $blocksize);
362                         last SWITCH;
363                 };
364                 $removeDev && do {
365                         $rc = removeDevice($handler, $removeDev);
366                         last SWITCH;
367                 };
368                 $addUser && do {
369                         $rc = addUser($group, $addUser);
370                         last SWITCH;
371                 };
372                 $removeUser && do {
373                         $rc = removeUser($group, $removeUser);
374                         last SWITCH;
375                 };
376                 defined($clearUsers) && do {
377                         $rc = clearUsers($group);
378                         last SWITCH;
379                 };
380                 $addGroup && do {
381                         $rc = addGroup($addGroup);
382                         last SWITCH;
383                 };
384                 $removeGroup && do {
385                         $rc = removeGroup($removeGroup);
386                         last SWITCH;
387                 };
388                 $assignDev && do {
389                         $rc = assignDevice($group, $assignDev, $devLun);
390                         last SWITCH;
391                 };
392                 $releaseDev && do {
393                         $rc = releaseDevice($group, $releaseDev);
394                         last SWITCH;
395                 };
396                 defined($clearDevs) && do {
397                         $rc = clearDevices($group);
398                         last SWITCH;
399                 };
400                 $enable && do {
401                         $enable = unformatTarget($enable);
402                         $rc = enableTarget($enable, $TRUE);
403                         last SWITCH;
404                 };
405                 $disable && do {
406                         $disable = unformatTarget($disable);
407                         $rc = enableTarget($disable, $FALSE);
408                         last SWITCH;
409                 };
410
411                 print "No valid operations specified.\n";
412                 usage();
413                 exit $TRUE;
414         }
415
416         print "All done.\n";
417
418         exit $rc;
419 }
420
421 sub readCurrentConfig {
422         print "Collecting current configuration.. ";
423
424         my $eHandlers = $SCST->handlers();
425
426         immediateExit($SCST->errorString());
427
428         foreach my $handler (@{$eHandlers}) {
429                 $HANDLERS{$handler}++; # For quick lookups
430         }
431
432         $TARGETS = targets();
433
434         $DEVICES = $SCST->devices();
435         immediateExit($SCST->errorString());
436
437         my $_eGroups = $SCST->groups();
438         immediateExit($SCST->errorString());
439
440         foreach my $group (@{$_eGroups}) {
441                 $GROUPS{$group}++; # For quick lookups
442                 $ASSIGNMENTS{$group} = $SCST->groupDevices($group);
443                 my $eUsers = $SCST->users($group);
444
445                 foreach my $user (@{$eUsers}) {
446                         $USERS{$group}->{$user}++; # For quick lookups
447                 }
448         }
449
450         print "done.\n";
451 }
452
453 sub writeConfiguration {
454         my $file = shift;
455
456         if (-f $file) {
457                 if (!unlink $file) {
458                         print "Failed to save current configuration, specified ".
459                           "file exists and cannot be deleted.\n";
460                         return 1;
461                 }
462         }
463
464         my $io = new IO::File $file, O_CREAT|O_WRONLY;
465
466         if (!$io) {
467                 print "Failed to save configuration to file '$file': $!\n";
468                 return 1;
469         }
470
471         print "Writing current configuration to file '$file'.. ";
472
473         print $io "# Automatically generated by $Version.\n\n";
474
475         # Device information
476         foreach my $handler (sort keys %HANDLERS) {
477                 next if ($SCST->handlerType($handler) != $SCST::SCST::IOTYPE_VIRTUAL);
478
479                 print $io "[HANDLER ".$_REVERSE_MAP_{$handler}."]\n";
480                 print $io "#DEVICE <vdisk name>,<device path>,<options>,<block size>\n";
481
482                 my $devices = $SCST->handlerDevices($handler);
483
484                 immediateExit($SCST->errorString());
485
486                 foreach my $device (sort keys %{$devices}) {
487                         my $options = $$devices{$device}->{'OPTIONS'};
488                         $options =~ s/\,/\|/;
489
490                         print $io "DEVICE $device,".$$devices{$device}->{'PATH'};
491                         print $io ",$options";
492                         print $io ",".$$devices{$device}->{'BLOCKSIZE'};
493                         print $io "\n";
494                 }
495
496                 print $io "\n";
497         }
498
499         # User configuration
500         foreach my $group (sort keys %USERS) {
501                 print $io "[GROUP $group]\n";
502                 print $io "#USER <user wwn>\n";
503
504                 foreach my $user (keys %{$USERS{$group}}) {
505                         print $io "USER $user\n";
506                 }
507
508                 print $io "\n";
509         }
510
511         # Assignments configuration
512         foreach my $group (sort keys %ASSIGNMENTS) {
513                 print $io "[ASSIGNMENT $group]\n";
514                 print $io "#DEVICE <device name>,<lun>\n";
515
516                 my $pointer = $ASSIGNMENTS{$group};
517                 foreach my $device (sort keys %{$pointer}) {
518                         print $io "DEVICE $device,".$$pointer{$device}."\n";
519                 }
520
521                 print $io "\n";
522         }
523
524         # Targets configuration
525         foreach my $type ('enable', 'disable') {
526                 print $io "[TARGETS $type]\n";
527                 print $io "#HOST <wwn identifier>\n";
528
529                 foreach my $target (sort keys %{$TARGETS}) {
530                         if ((($type eq 'enable') && $$TARGETS{$target}->{'enabled'}) ||
531                             (($type eq 'disable') && !$$TARGETS{$target}->{'enabled'})) {
532                                 my $f_target = formatTarget($target);
533                                 print $io "HOST $f_target\n" if (!$$TARGETS{$target}->{'duplicate'});
534                         }
535                 }
536
537                 print $io "\n";
538         }
539
540         print "done\n";
541
542         close $io;
543
544         return 0;
545 }
546
547 sub applyConfiguration {
548         my $confile = shift;
549         my $force = shift;
550         my $check = shift;
551         my $config = readConfig($confile);
552         my $errs;
553         my $changes = 0;
554
555         my %used_devs;
556         my %used_users;
557         my %used_assignments;
558
559         # Cache device/handler configuration
560         foreach my $entry (keys %{$$config{'HANDLER'}}) {
561                 foreach my $device (@{$$config{'HANDLER'}->{$entry}->{'DEVICE'}}) {
562                         my($vname, undef) = split(/\,/, $device, 2);
563                         $vname = cleanupString($vname);
564                         $used_devs{$vname} = $entry;
565                 }
566         }
567
568         # Cache user/group configuration
569         foreach my $group (keys %{$$config{'GROUP'}}) {
570                 foreach my $user (@{$$config{'GROUP'}->{$group}->{'USER'}}) {
571                         $used_users{$group}->{$user}++;
572                 }
573         }
574
575         # Cache device association configuration
576         foreach my $group (keys %{$$config{'ASSIGNMENT'}}) {
577                 foreach my $device (@{$$config{'ASSIGNMENT'}->{$group}->{'DEVICE'}}) {
578                         my($vname, $lun) = split(/\,/, $device);
579                         $vname = cleanupString($vname);
580                         $used_assignments{$group}->{$vname} = $lun;
581                 }
582         }
583
584         # If -ForceConfig is used, check for configurations which we've deleted but are still active.
585         if ($force || $check) {
586                 # Associations
587                 foreach my $group (sort keys %ASSIGNMENTS) {
588                         if (!defined($used_assignments{$group}) && (keys %{$ASSIGNMENTS{$group}})) {
589                                 print "\tWARNING: Group $group has no associations in saved configuration";
590
591                                 if (!$check) {
592                                         print ", clearing all associations.\n";
593                                         if (clearDevices($group)) {
594                                                 $errs++;
595                                         } else {
596                                                 $changes++;
597                                         }
598                                 } else {
599                                         print ".\n";
600                                         $changes++;
601                                 }
602                         } else {
603                                 my $_assignments = $ASSIGNMENTS{$group};
604
605                                 foreach my $device (sort keys %{$_assignments}) {
606                                         if (!defined($used_assignments{$group}->{$device})) {
607                                                 print "\tWARNING: Device $device is not associated with group ".
608                                                   "$group in saved configuration";
609
610                                                 if (!$check) {
611                                                         print ", releasing.\n";
612                                                         if (releaseDevice($group, $device)) {
613                                                                 $errs++;
614                                                         } else {
615                                                                 $changes++;
616                                                         }
617                                                 } else {
618                                                         print ".\n";
619                                                         $changes++;
620                                                 }
621                                         }
622                                 }
623                         }
624                 }
625
626                 # Users & Groups
627                 foreach my $group (sort keys %USERS) {
628                         if (!defined($used_users{$group})) {
629                                 print "\tWARNING: Group $group does not exist in saved configuration";
630
631                                 if (!$check) {
632                                         print ", removing.\n";
633                                         if (clearUsers($group)) {
634                                                 $errs++;
635                                         } else {
636                                                 $changes++;
637                                         }
638
639                                         if (removeGroup($group)) {
640                                                 $errs++;
641                                         } else {
642                                                 $changes++;
643                                         }
644                                 } else {
645                                         print ".\n";
646                                         $changes++;
647                                 }
648                         } else {
649                                 foreach my $user (sort keys %{$USERS{$group}}) {
650                                         if (!defined($used_users{$group}->{$user})) {
651                                                 print "\WARNING: User $user is not defined as part of group $group ".
652                                                   "in saved configuration";
653
654                                                 if (!$check) {
655                                                         print ", removing.\n";
656                                                         if (removeUser($group, $user)) {
657                                                                 $errs++;
658                                                         } else {
659                                                                 $changes++;
660                                                         }
661                                                 } else {
662                                                         print ".\n";
663                                                         $changes++;
664                                                 }
665                                         }
666                                 }
667                         }
668                 }
669
670                 # Devices
671                 foreach my $device (sort keys %{$DEVICES}) {
672                         next if ($SCST->handlerType($$DEVICES{$device}) != $SCST::SCST::IOTYPE_VIRTUAL);
673
674                         if ($$DEVICES{$device} && !defined($used_devs{$device})) {
675                                 # Device gone, but is it still assigned to a group?
676                                 my $isAssigned = $FALSE;
677                                 foreach my $group (sort keys %used_assignments) {
678                                         if (defined($used_assignments{$group}->{$device})) {
679                                                 print "\tWARNING: Device $device is not defined in saved configuration, ".
680                                                   "however, it is still assigned to group $group! Ignoring removal.\n";
681                                                 $isAssigned = $TRUE;
682                                         }
683                                 }
684
685                                 if (!$isAssigned) {
686                                         print "\tWARNING: Device $device is not defined in saved configuration";
687
688                                         if (!$check) {
689                                                 print ", removing.\n";
690                                                 if (removeDevice($device)) {
691                                                         $errs++;
692                                                 } else {
693                                                         $changes++;
694                                                 }
695                                         } else {
696                                                 print ".\n";
697                                                 $changes++;
698                                         }
699                                 }
700                         } else {
701                                 # Handler change
702                                 if ($_HANDLER_MAP_{$used_devs{$device}} != $$DEVICES{$device}) {
703                                         my $handler = $used_devs{$device};
704
705                                         if ($HANDLERS{$_HANDLER_MAP_{$handler}}) {
706                                                 print "\tWARNING: Device $device changes handler to $handler";
707
708                                                 if (!$check) {
709                                                         print ", changing.\n";
710                                                         if (assignDeviceToHandler($device, $handler)) {
711                                                                 $errs++;
712                                                         } else {
713                                                                 $changes++;
714                                                         }
715                                                 } else {
716                                                         print ".\n";
717                                                         $changes++;
718                                                 }
719                                         }
720                                 }
721                         }
722                 }
723         }
724
725         print "Applying configurations additions..\n" if (!$check);
726         print "\n";
727
728         readCurrentConfig() if ($force);
729
730         foreach my $vname (keys %used_devs) {
731                 my $_handler = $used_devs{$vname};
732
733                 if (!$HANDLERS{$_HANDLER_MAP_{$_handler}}) {
734                         print "\t-> WARNING: Handler '$_handler' does not exist.\n";
735                         $errs += 1;
736                         next;
737                 }
738
739                 foreach my $device (@{$$config{'HANDLER'}->{$_handler}->{'DEVICE'}}) {
740                         my(undef, $path, $options, $blocksize) = split(/\,/, $device);
741                         $path = cleanupString($path);
742                         $options =~ s/\s+//; $options =~ s/\|/,/;
743
744                         next if (defined($$DEVICES{$vname}));
745
746                         if ($check) {
747                                 print "\t-> New device '$_handler:$vname' at path '$path', options '$options', blocksize $blocksize.\n";
748                                 $$DEVICES{$vname} = $_HANDLER_MAP_{$_handler};
749                                 $changes++;
750                         } else {
751                                 if (addDevice($_handler, $vname, $path, $options, $blocksize)) {
752                                         $errs++;
753                                 } else {
754                                         $changes++;
755                                 }
756                         }
757                 }
758         }
759
760         # Create new groups and add users..
761         foreach my $group (keys %used_users) {
762                 if (!defined($GROUPS{$group})) {
763                         if ($check) {
764                                 print "\t-> New group definition '$group.'\n";
765                                 $GROUPS{$group}++;
766                                 $changes++;
767                         } else {
768                                 if (addGroup($group)) {
769                                         $errs++;
770                                 } else {
771                                         $changes++;
772                                 }
773                         }
774                 }
775
776                 foreach my $user (keys %{$used_users{$group}}) {
777                         if (!defined($USERS{$group}->{$user})) {
778                                 if ($check) {
779                                         print "\t-> New user definition '$user' for group '$group'.\n";
780                                         $USERS{$group}->{$user}++;
781                                         $changes++;
782                                 } else {
783                                         if (addUser($group, $user)) {
784                                                 $errs++;
785                                         } else {
786                                                 $changes++;
787                                         }
788                                 }
789                         }
790                 }
791         }
792
793         # Assign new devices to groups..
794         foreach my $group (keys %used_assignments) {
795                 if (!defined($GROUPS{$group})) {
796                         print "\t-> WARNING: Unable to assign to non-existant group '$group'.\n";
797                         $errs += 1;
798                         next;
799                 }
800
801                 foreach my $vname (keys %{$used_assignments{$group}}) {
802                         my $lun = $used_assignments{$group}->{$vname};
803                         my $_assignments = $ASSIGNMENTS{$group};
804                         next if (defined($$_assignments{$vname}));
805
806                         if ($check) {
807                                 $lun = 'auto' if (!defined($lun));
808                                 print "\t-> New device assignment for '$vname' to group '$group' at LUN $lun.\n";
809                                 $changes++;
810                         } else {
811                                 if (assignDevice($group, $vname, $lun)) {
812                                         $errs++;
813                                 } else {
814                                         $changes++;
815                                 }
816                         }
817                 }
818         }
819
820         # Enable/Disable configured targets
821         foreach my $type (keys %{$$config{'TARGETS'}}) {
822                 my $enable;
823
824                 if ($type eq 'enable') {
825                         $enable = $TRUE;
826                 } elsif ($type eq 'disable') {
827                         $enable = $FALSE;
828                 } else {
829                         print "\t-> WARNING: Ignoring invalid TARGETS specifier '$type'. ".
830                           "Should be one of enable,disable.\n";
831                         next;
832                 }
833
834                 foreach my $target (@{$$config{'TARGETS'}->{$type}->{'HOST'}}) {
835                         my $i_target = unformatTarget($target);
836
837                         if (!defined($$TARGETS{$i_target})) {
838                                 print "\t-> WARNING: Target '$target' not found on system.\n";
839                                 $errs += 1;
840                                 next;
841                         }
842
843                         next if ($enable == targetEnabled($i_target));
844
845                         if (!$enable && targetEnabled($target)) {
846                                 if ($force || $check) {
847                                         print "\tWARNING: Target mode for '$target' is currently enabled, ".
848                                           "however configuration file wants it disabled";
849
850                                         if (!$check) {
851                                                 print ", disabling.\n";
852                                                 if (enableTarget($target, $enable)) {
853                                                         $errs++;
854                                                 } else {
855                                                         $changes++;
856                                                 }
857                                         } else {
858                                                 print ".\n";
859                                                 $changes++;
860                                         }
861                                 }
862                         } else {
863                                 print "\t-> Target '$target' is enabled in configuration file, ".
864                                   "however is currently disabled";
865
866                                 if (!$check) {
867                                         print ", enabling.\n";
868                                         if (enableTarget($target, $enable)) {
869                                                 $errs++;
870                                         } else {
871                                                 $changes++;
872                                         }
873                                 } else {
874                                         print ".\n";
875                                         $changes++;
876                                 }
877                         }
878                 }
879         }
880
881         print "\nEncountered $errs error(s) while processing.\n" if ($errs);
882
883         if ($check) {
884                 print "Configuration checked, $changes difference(s) found with current configuration.\n";
885         } else {
886                 $changes = 0 if ($_DEBUG_);
887                 print "Configuration applied, $changes changes made.\n";
888         }
889
890         return $TRUE if ($errs);
891         return $FALSE;
892 }
893
894 sub clearConfiguration {
895         my $errs;
896
897         print "WARNING: This removes ALL applied SCST configuration and may result in data loss!\n";
898         print "If this is not what you intend, press ctrl-c now. Waiting 10 seconds.\n\n";
899         sleep 10;
900
901         print "\nRemoving all user and groups:\n\n";
902         foreach my $group (keys %GROUPS) {
903                 $errs += removeGroup($group) if ($group ne $_DEFAULT_GROUP_);
904         }
905
906         print "\nRemoving all handler devices:\n\n";
907         foreach my $device (keys %{$DEVICES}) {
908                 next if ($SCST->handlerType($$DEVICES{$device}) != $SCST::SCST::IOTYPE_VIRTUAL);
909                 $errs += removeDevice($_REVERSE_MAP_{$$DEVICES{$device}}, $device);
910         }
911
912         print "\nEncountered $errs error(s) while processing.\n" if ($errs);
913         print "\nConfiguration cleared.\n";
914
915         return $TRUE if ($errs);
916         return $FALSE;
917 }
918
919 sub showSessions {
920         my $sessions = $SCST->sessions();
921         immediateExit($SCST->errorString());
922
923         print "\n\tTarget Name\tInitiator Name\t\t\tGroup Name\t\tCommand Count\n";
924
925         foreach my $target (keys %{$sessions}) {
926                 foreach my $group (keys %{$$sessions{$target}}) {
927                         foreach my $user (keys %{$$sessions{$target}->{$group}}) {
928                                 my $commands = $$sessions{$target}->{$group}->{$user};
929
930                                 print "\t$target\t$user\t\t$group\t\t$commands\n";
931                         }
932                 }
933         }
934
935         print "\n";
936
937         return $FALSE;
938 }
939
940 sub addDevice {
941         my $handler = shift;
942         my $device = shift;
943         my $path = shift;
944         my $options = shift;
945         my $blocksize = shift;
946
947         $options =~ s/\,/ /;
948
949         my $_handler = $_HANDLER_MAP_{$handler};
950         my $htype = $SCST->handlerType($_handler);
951
952         if (!$htype) {
953                 print "WARNING: Internal error occured: ".$SCST->errorString()."\n";
954                 return $TRUE;
955         }
956
957         if ($htype != $SCST::SCST::IOTYPE_VIRTUAL) {
958                 my $typeString = $_HANDLER_TYPE_MAP_{$htype};
959                 my $validType = $_HANDLER_TYPE_MAP_{$SCST::SCST::IOTYPE_VIRTUAL};
960                 print "WARNING: Handler $handler of type $typeString is incapable of ".
961                   "opening/closing devices. Valid handlers are:\n".
962                   validHandlerTypes($SCST::SCST::IOTYPE_VIRTUAL)."\n";
963                 return $TRUE;
964         }
965
966         if (defined($$DEVICES{$device})) {
967                 print "WARNING: Device '$device' already defined.\n";
968                 return $TRUE;
969         }
970
971         print "\t-> Opening virtual device '$device' at path '$path' using handler '$handler'..\n";
972
973         if ($SCST->openDevice($_handler, $device, $path, $options, $blocksize)) {
974                 print "WARNING: Failed to open virtual device '$device' at path '$path': ".
975                   $SCST->errorString()."\n";
976                 return $TRUE;
977         }
978
979         $$DEVICES{$device} = $_handler;
980
981         return $FALSE;
982 }
983
984 sub removeDevice {
985         my $handler = shift;
986         my $device = shift;
987
988         my $_handler = $_HANDLER_MAP_{$handler};
989         my $htype = $SCST->handlerType($_handler);
990
991         if (!$htype) {
992                 print "WARNING: Internal error occured: ".$SCST->errorString()."\n";
993                 return $TRUE;
994         }
995
996         if ($htype != $SCST::SCST::IOTYPE_VIRTUAL) {
997                 my $typeString = $_HANDLER_TYPE_MAP_{$htype};
998                 my $validType = $_HANDLER_TYPE_MAP_{$SCST::SCST::IOTYPE_VIRTUAL};
999                 print "WARNING: Handler $handler of type $typeString is incapable of ".
1000                   "opening/closing devices. Valid handlers are:\n".
1001                   validHandlerTypes($SCST::SCST::IOTYPE_VIRTUAL)."\n";
1002                 return $TRUE;
1003         }
1004
1005         if (!defined($$DEVICES{$device})) {
1006                 print "WARNING: Device '$device' not defined.\n";
1007                 return $TRUE;
1008         }
1009
1010         print "\t-> Closing virtual device '$device'..\n";
1011
1012         if ($SCST->closeDevice($_handler, $device)) {
1013                 print "WARNING: Failed to close virtual device '$device': ".
1014                   $SCST->errorString()."\n";
1015                 return $TRUE;
1016         }
1017
1018         undef $$DEVICES{$device};
1019
1020         return $FALSE;
1021 }
1022
1023 sub addGroup {
1024         my $group = shift;
1025
1026         if (defined($GROUPS{$group})) {
1027                 print "WARNING: Group '$group' already exists.\n";
1028                 return $TRUE;
1029         }
1030
1031         print "\t-> Creating security group '$group'..\n";
1032
1033         if ($SCST->addGroup($group)) {
1034                 print "WARNING: Failed to create security group '$group': ".
1035                   $SCST->errorString()."\n";
1036                 return $TRUE;
1037         }
1038
1039         $GROUPS{$group}++;
1040
1041         return $FALSE;
1042 }
1043
1044 sub removeGroup {
1045         my $group = shift;
1046
1047         if (!defined($GROUPS{$group})) {
1048                 print "WARNING: Group '$group' does not exist.\n";
1049                 return $TRUE;
1050         }
1051
1052         print "\t-> Removing security group '$group'..\n";
1053
1054         if ($SCST->removeGroup($group)) {
1055                 print "WARNING: Failed to remove security group '$group': ".
1056                   $SCST->errorString()."\n";
1057                 return $TRUE;
1058         }
1059
1060         undef $GROUPS{$group};
1061
1062         return $FALSE;
1063 }
1064
1065 sub addUser {
1066         my $group = shift;
1067         my $user = shift;
1068
1069         if (!defined($GROUPS{$group})) {
1070                 print "WARNING: Failed to add user '$user' to group '$group', group does not exist.\n";
1071                 return $TRUE;
1072         }
1073
1074         if (defined($USERS{$group}->{$user})) {
1075                 print "WARNING: User '$user' already exists in security group '$group'.\n";
1076                 return $TRUE;
1077         }
1078
1079         print "\t-> Adding user '$user' to security group '$group'..\n";
1080
1081         if ($SCST->addUser($user, $group)) {
1082                 print "WARNING: Failed to add user '$user' to security group '$group': ".
1083                   $SCST->errorString()."\n";
1084                 return $TRUE;
1085         }
1086
1087         $USERS{$group}->{$user}++;
1088
1089         return $FALSE;
1090 }
1091
1092 sub removeUser {
1093         my $group = shift;
1094         my $user = shift;
1095
1096         if (!defined($GROUPS{$group})) {
1097                 print "WARNING: Failed to remove user '$user' from group '$group', group does not exist.\n";
1098                 return $TRUE;
1099         }
1100
1101         if (!defined($USERS{$group}->{$user})) {
1102                 print "WARNING: User '$user' doesn\'t exist in security group '$group'.\n";
1103                 return $TRUE;
1104         }
1105
1106         print "\t-> Removing user '$user' from security group '$group'..\n";
1107
1108         if ($SCST->removeUser($user, $group)) {
1109                 print "WARNING: Failed to add user '$user' to security group '$group': ".
1110                   $SCST->errorString()."\n";
1111                 return $TRUE;
1112         }
1113
1114         undef $USERS{$group}->{$user};
1115
1116         return $FALSE;
1117 }
1118
1119 sub clearUsers {
1120         my $group = shift;
1121
1122         if (!defined($GROUPS{$group})) {
1123                 print "WARNING: Failed to clear users from group '$group', group does not exist.\n";
1124                 return $TRUE;
1125         }
1126
1127         print "\t-> Clearing users from security group '$group'..\n";
1128
1129         if ($SCST->clearUsers($group)) {
1130                 print "WARNING: Failed to clear users from security group '$group': ".
1131                   $SCST->errorString()."\n";
1132                 return $TRUE;
1133         }
1134
1135         undef $USERS{$group};
1136
1137         return $FALSE;
1138 }
1139
1140 sub assignDevice {
1141         my $group = shift;
1142         my $device = shift;
1143         my $lun = shift;
1144         my %allLuns;
1145
1146         # Put luns into something easier to parse..
1147         foreach my $_group (keys %ASSIGNMENTS) {
1148                 my $_gAssigns = $ASSIGNMENTS{$_group};
1149
1150                 foreach my $_device (keys %{$_gAssigns}) {
1151                         @{$allLuns{$_group}}[$$_gAssigns{$_device}] = $_device;
1152                 }
1153         }
1154
1155         # Use the next available LUN if none specified
1156         if ($lun !~ /\d+/) {
1157                 $lun = ($#{$allLuns{$group}} + 1);
1158                 if ($lun > $_MAX_LUNS_) {
1159                         print "ERROR: Unable to use next available LUN of $lun, lun out of range.\n";
1160                         return $TRUE;
1161                 }
1162
1163                 print "\t-> Device '$device': Using next available LUN of $lun for group '$group'.\n";
1164         }
1165                         
1166         if (($lun < 0) || ($lun > $_MAX_LUNS_)) {
1167                 print "ERROR: Unable to assign device '$device', lun '$lun' is out of range.\n";
1168                 return $TRUE;
1169         }
1170
1171         if (!defined($$DEVICES{$device})) {
1172                 print "WARNING: Unable to assign non-existant device '$device' to group '$group'.\n";
1173                 return $TRUE;
1174         }
1175
1176         if (@{$allLuns{$group}}[$lun]) {
1177                 print "ERROR: Device '$device': Lun '$lun' is already assigned to device '".@{$allLuns{$group}}[$lun]."'.\n";
1178                 return $TRUE;
1179         }
1180
1181         print "\t-> Assign virtual device '$device' to group '$group' at LUN '$lun'..\n";
1182
1183         if ($SCST->assignDeviceToGroup($device, $group, $lun)) {
1184                 print "WARNING: Failed to assign device '$device' to group '$group': ".
1185                   $SCST->errorString()."\n";
1186                 return $TRUE;
1187         }
1188
1189         if (!defined($ASSIGNMENTS{$group})) {
1190                 my %assignments_t;
1191                 $ASSIGNMENTS{$group} = \%assignments_t;
1192         }
1193
1194         my $_assignments = $ASSIGNMENTS{$group};
1195
1196         $$_assignments{$device} = $lun; 
1197
1198         return $FALSE;
1199 }
1200
1201 sub releaseDevice {
1202         my $group = shift;
1203         my $device = shift;
1204
1205         if (!defined($GROUPS{$group})) {
1206                 print "WARNING: Failed to release device '$device' from group '$group', group does not exist.\n";
1207                 return $TRUE;
1208         }
1209
1210         if (!defined($$DEVICES{$device})) {
1211                 print "WARNING: Failed to release device '$device', device not defined.\n";
1212                 return $TRUE;
1213         }
1214
1215         print "\t-> Release virtual device '$device' from group '$group'..\n";
1216
1217         if ($SCST->removeDeviceFromGroup($device, $group)) {
1218                 print "WARNING: Failed to release device '$device' from group '$group': ".
1219                   $SCST->errorString()."\n";
1220                 return $TRUE;
1221         }
1222
1223         my $_assignments = $ASSIGNMENTS{$group};
1224
1225         undef $$_assignments{$device};
1226
1227         return $FALSE;
1228 }
1229
1230 sub clearDevices {
1231         my $group = shift;
1232
1233         if (!defined($GROUPS{$group})) {
1234                 print "WARNING: Failed to clear devices from group '$group', group does not exist.\n";
1235                 return $TRUE;
1236         }
1237
1238         print "\t-> Clear virtual devices from group '$group'..\n";
1239
1240         if ($SCST->clearGroupDevices($group)) {
1241                 print "WARNING: Failed to clear devices from group '$group': ".
1242                   $SCST->errorString()."\n";
1243                 return $TRUE;
1244         }
1245
1246         undef $ASSIGNMENTS{$group};
1247
1248         return $FALSE;
1249 }
1250
1251 sub targets {
1252         my %targets;
1253         my %fcards;
1254         my $root = new IO::Dir $_FC_CLASS_;
1255
1256         if ($root) {
1257                 while (my $entry = $root->read()) {
1258                         next if (($entry eq '.') || ($entry eq '..'));
1259
1260                         my $io = new IO::File "$_FC_CLASS_/$entry/port_name", O_RDONLY;
1261
1262                         if ($io) {
1263                                 my $wwn = <$io>;
1264                                 chomp $wwn;
1265                                 close $io;
1266
1267                                 $fcards{$entry} = $wwn;
1268                         }
1269                 }
1270         }
1271
1272         $root = new IO::Dir $_SCSI_CLASS_;
1273
1274         if ($root) {
1275                 while (my $entry = $root->read()) {
1276                         next if (($entry eq '.') || ($entry eq '..'));
1277
1278                         my $io = new IO::File "$_SCSI_CLASS_/$entry/target_mode_enabled", O_RDONLY;
1279
1280                         if ($io) {
1281                                 my $enabled = <$io>;
1282                                 chomp $enabled;
1283                                 close $io;
1284
1285                                 $targets{$entry}->{'path'} = "$_SCSI_CLASS_/$entry/target_mode_enabled";
1286                                 $targets{$entry}->{'enabled'} = $enabled;
1287
1288                                 if ($fcards{$entry}) {
1289                                         $targets{$fcards{$entry}}->{'enabled'} = $enabled;
1290                                         $targets{$fcards{$entry}}->{'path'} =
1291                                           "$_SCSI_CLASS_/$entry/target_mode_enabled";
1292                                         $targets{$entry}->{'duplicate'} = $TRUE;
1293                                 } else {
1294                                         $targets{$entry}->{'duplicate'} = $FALSE;
1295                                 }
1296                         }
1297                 }
1298         }
1299
1300         return \%targets;
1301 }
1302
1303 sub targetEnabled {
1304         my $target = shift;
1305
1306         return undef if (!defined($$TARGETS{$target}));
1307         return $$TARGETS{$target}->{'enabled'};
1308 }
1309
1310 sub enableTarget {
1311         my $target = shift;
1312         my $enable = shift;
1313
1314         return undef if (!defined($$TARGETS{$target}));
1315
1316         my $io = new IO::File $$TARGETS{$target}->{'path'}, O_WRONLY;
1317         return $TRUE if (!$io);
1318
1319         print $enable ? "\t-> Enabling" : "\t-> Disabling";
1320         print " target mode for SCST host '$target'.\n";
1321
1322         if ($_DEBUG_) {
1323                 print "DBG($$): ".$$TARGETS{$target}->{'path'}." -> $enable\n\n";
1324         } else {
1325                 print $io $enable;
1326         }
1327
1328         close $io;
1329
1330         $$TARGETS{unformatTarget($target)}->{'enabled'} = $enable;
1331
1332         return $FALSE;
1333 }
1334
1335 sub readConfig {
1336         my $confile = shift;
1337         my %config;
1338         my $section;
1339         my $arg;
1340
1341         my $io = new IO::File $confile, O_RDONLY;
1342
1343         die("FATAL: Unable to open specified configuration file $confile: $!\n") if (!$io);
1344
1345         while (my $line = <$io>) {
1346                 ($line, undef) = split(/\#/, $line, 2);
1347                 $line = cleanupString($line);
1348
1349                 if ($line =~ /^\[(.*)\]$/) {
1350                         ($section, $arg) = split(/\s+/, $1, 2);
1351                 } elsif ($section && $arg && $line) {
1352                         my($parameter, $value) = split(/\s+/, $line, 2);
1353                         push @{$config{$section}->{$arg}->{$parameter}}, $value;
1354                 }
1355         }
1356
1357         close $io;
1358
1359         return \%config;
1360 }
1361
1362 sub cleanupString {
1363         my $string = shift;
1364
1365         $string =~ s/^\s+//;
1366         $string =~ s/\s+$//;
1367
1368         return $string;
1369 }
1370
1371 sub formatTarget {
1372         my $target = shift;
1373
1374         if ($target =~ /^0x/) {
1375                 $target =~ s/^0x//;
1376                 my($o1, $o2, $o3, $o4, $o5, $o6, $o7, $o8) = unpack("A2A2A2A2A2A2A2A2", $target);
1377                 $target = "$o1:$o2:$o3:$o4:$o5:$o6:$o7:$o8";
1378         }
1379
1380         $target =~ tr/A-Z/a-z/;
1381
1382         return $target;
1383 }
1384
1385 sub unformatTarget {
1386         my $target = shift;
1387
1388         if ($target =~ /^.{2}\:.{2}\:.{2}\:.{2}\:.{2}\:.{2}\:.{2}\:.{2}/) {
1389                 $target =~ s/\://g;
1390                 $target = "0x$target";
1391         }
1392
1393         $target =~ tr/A-Z/a-z/;
1394
1395         return $target;
1396 }
1397
1398 sub validHandlerTypes {
1399         my $type = shift;
1400         my $buffer = "\n";
1401
1402         foreach my $handler (keys %_REVERSE_MAP_) {
1403                 $buffer .= "\t".$_REVERSE_MAP_{$handler}."\n" if ($SCST->handlerType($handler) == $type);
1404         }
1405
1406         return $buffer;
1407 }
1408
1409 # If we have an unread error from SCST, exit immediately
1410 sub immediateExit {
1411         my $error = shift;
1412
1413         return if (!$error);
1414
1415         print "\n\nFATAL: Received the following error:\n\n\t";
1416         print "$error\n\n";
1417
1418         exit 1;
1419 }
1420
1421 # Hey! Stop that!
1422 sub commitSuicide {
1423         print "\n\nAborting immediately.\n";
1424         exit 1;
1425 }