- Fix really stupid mistake where config commits were using same path
[mirror/scst/.git] / scstadmin / scstadmin
1 #!/usr/bin/perl
2 $Version  = 'SCST Configurator v0.7.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, BIO
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         readWorkingConfig();
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                         readWorkingConfig();
342                         $rc = applyConfiguration($applyConfig, $forceConfig, $FALSE);
343                         last SWITCH;
344                 };
345                 $checkConfig && do {
346                         $rc = applyConfiguration($checkConfig, $FALSE, $TRUE);
347                         last SWITCH;
348                 };
349                 $writeConfig && do {
350                         $rc = writeConfiguration($writeConfig);
351                         last SWITCH;
352                 };
353                 $showSessions && do {
354                         $rc = showSessions();
355                         last SWITCH;
356                 };
357                 defined($clearConfig) && do {
358                         $rc = clearConfiguration();
359                         last SWITCH;
360                 };
361                 $addDev && do {
362                         $rc = addDevice($handler, $addDev, $devPath, $options, $blocksize);
363                         last SWITCH;
364                 };
365                 $removeDev && do {
366                         $rc = removeDevice($handler, $removeDev);
367                         last SWITCH;
368                 };
369                 $addUser && do {
370                         $rc = addUser($group, $addUser);
371                         last SWITCH;
372                 };
373                 $removeUser && do {
374                         $rc = removeUser($group, $removeUser);
375                         last SWITCH;
376                 };
377                 defined($clearUsers) && do {
378                         $rc = clearUsers($group);
379                         last SWITCH;
380                 };
381                 $addGroup && do {
382                         $rc = addGroup($addGroup);
383                         last SWITCH;
384                 };
385                 $removeGroup && do {
386                         $rc = removeGroup($removeGroup);
387                         last SWITCH;
388                 };
389                 $assignDev && do {
390                         $rc = assignDevice($group, $assignDev, $devLun);
391                         last SWITCH;
392                 };
393                 $releaseDev && do {
394                         $rc = releaseDevice($group, $releaseDev);
395                         last SWITCH;
396                 };
397                 defined($clearDevs) && do {
398                         $rc = clearDevices($group);
399                         last SWITCH;
400                 };
401                 $enable && do {
402                         $enable = unformatTarget($enable);
403                         $rc = enableTarget($enable, $TRUE);
404                         last SWITCH;
405                 };
406                 $disable && do {
407                         $disable = unformatTarget($disable);
408                         $rc = enableTarget($disable, $FALSE);
409                         last SWITCH;
410                 };
411
412                 print "No valid operations specified.\n";
413                 usage();
414                 exit $TRUE;
415         }
416
417         print "All done.\n";
418
419         exit $rc;
420 }
421
422 sub readWorkingConfig {
423         print "Collecting current configuration.. ";
424
425         $TARGETS  = undef;
426         $DEVICES  = undef;
427         %HANDLERS = ();
428         %GROUPS   = ();
429         %USERS    = ();
430
431         my $eHandlers = $SCST->handlers();
432
433         immediateExit($SCST->errorString());
434
435         foreach my $handler (@{$eHandlers}) {
436                 $HANDLERS{$handler}++; # For quick lookups
437         }
438
439         $TARGETS = targets();
440
441         $DEVICES = $SCST->devices();
442         immediateExit($SCST->errorString());
443
444         my $_eGroups = $SCST->groups();
445         immediateExit($SCST->errorString());
446
447         foreach my $group (@{$_eGroups}) {
448                 $GROUPS{$group}++;
449                 $ASSIGNMENTS{$group} = $SCST->groupDevices($group);
450                 my $eUsers = $SCST->users($group);
451
452                 foreach my $user (@{$eUsers}) {
453                         $USERS{$group}->{$user}++; # For quick lookups
454                 }
455         }
456
457         print "done.\n\n";
458 }
459
460 sub writeConfiguration {
461         my $file = shift;
462
463         if (-f $file) {
464                 if (!unlink $file) {
465                         print "Failed to save current configuration, specified ".
466                           "file exists and cannot be deleted.\n";
467                         return 1;
468                 }
469         }
470
471         my $io = new IO::File $file, O_CREAT|O_WRONLY;
472
473         if (!$io) {
474                 print "Failed to save configuration to file '$file': $!\n";
475                 return 1;
476         }
477
478         print "Writing current configuration to file '$file'.. ";
479
480         print $io "# Automatically generated by $Version.\n\n";
481
482         # Device information
483         foreach my $handler (sort keys %HANDLERS) {
484                 print $io "[HANDLER ".$_REVERSE_MAP_{$handler}."]\n";
485
486                 if ($SCST->handlerType($handler) == $SCST::SCST::IOTYPE_VIRTUAL) {
487                         print $io "#DEVICE <vdisk name>,<device path>";
488                         if ($handler == $SCST::SCST::VDISK_TYPE) {
489                                 print $io ",<options>,<block size>\n";
490                         } else {
491                                 print $io "\n";
492                         }
493                 } else {
494                         print $io "#DEVICE <H:C:I:L>\n";
495                 }
496
497                 my $devices = $SCST->handlerDevices($handler);
498
499                 immediateExit($SCST->errorString());
500
501                 foreach my $device (sort keys %{$devices}) {
502                         my $options = $$devices{$device}->{'OPTIONS'};
503                         $options =~ s/\,/\|/;
504
505                         print $io "DEVICE $device,".$$devices{$device}->{'PATH'};
506                         print $io ",$options";
507                         print $io ",".$$devices{$device}->{'BLOCKSIZE'};
508                         print $io "\n";
509                 }
510
511                 print $io "\n";
512         }
513
514         # User configuration
515         foreach my $group (sort keys %USERS) {
516                 print $io "[GROUP $group]\n";
517                 print $io "#USER <user wwn>\n";
518
519                 foreach my $user (keys %{$USERS{$group}}) {
520                         print $io "USER $user\n";
521                 }
522
523                 print $io "\n";
524         }
525
526         # Assignments configuration
527         foreach my $group (sort keys %ASSIGNMENTS) {
528                 print $io "[ASSIGNMENT $group]\n";
529                 print $io "#DEVICE <device name>,<lun>\n";
530
531                 my $pointer = $ASSIGNMENTS{$group};
532                 foreach my $device (sort keys %{$pointer}) {
533                         print $io "DEVICE $device,".$$pointer{$device}."\n";
534                 }
535
536                 print $io "\n";
537         }
538
539         # Targets configuration
540         foreach my $type ('enable', 'disable') {
541                 print $io "[TARGETS $type]\n";
542                 print $io "#HOST <wwn identifier>\n";
543
544                 foreach my $target (sort keys %{$TARGETS}) {
545                         if ((($type eq 'enable') && $$TARGETS{$target}->{'enabled'}) ||
546                             (($type eq 'disable') && !$$TARGETS{$target}->{'enabled'})) {
547                                 my $f_target = formatTarget($target);
548                                 print $io "HOST $f_target\n" if (!$$TARGETS{$target}->{'duplicate'});
549                         }
550                 }
551
552                 print $io "\n";
553         }
554
555         print "done\n";
556
557         close $io;
558
559         return 0;
560 }
561
562 sub applyConfiguration {
563         my $confile = shift;
564         my $force = shift;
565         my $check = shift;
566         my $config = readConfig($confile);
567         my $errs;
568         my $changes = 0;
569
570         my %used_devs;
571         my %used_users;
572         my %used_assignments;
573
574         # Cache device/handler configuration
575         foreach my $entry (keys %{$$config{'HANDLER'}}) {
576                 foreach my $device (@{$$config{'HANDLER'}->{$entry}->{'DEVICE'}}) {
577                         my($vname, undef) = split(/\,/, $device, 2);
578                         $vname = cleanupString($vname);
579                         $used_devs{$vname} = $entry;
580                 }
581         }
582
583         # Cache user/group configuration
584         foreach my $group (keys %{$$config{'GROUP'}}) {
585                 foreach my $user (@{$$config{'GROUP'}->{$group}->{'USER'}}) {
586                         $used_users{$group}->{$user}++;
587                 }
588         }
589
590         # Cache device association configuration
591         foreach my $group (keys %{$$config{'ASSIGNMENT'}}) {
592                 foreach my $device (@{$$config{'ASSIGNMENT'}->{$group}->{'DEVICE'}}) {
593                         my($vname, $lun) = split(/\,/, $device);
594                         $vname = cleanupString($vname);
595                         $used_assignments{$group}->{$vname} = $lun;
596                 }
597         }
598
599         # If -ForceConfig is used, check for configurations which we've deleted but are still active.
600         if ($force || $check) {
601                 # Associations
602                 foreach my $group (sort keys %ASSIGNMENTS) {
603                         if (!defined($used_assignments{$group}) && (keys %{$ASSIGNMENTS{$group}})) {
604                                 print "\t-> WARNING: Group '$group' has no associations in saved configuration";
605
606                                 if (!$check) {
607                                         print ", clearing all associations.\n";
608                                         if (clearDevices($group)) {
609                                                 $errs++;
610                                         } else {
611                                                 $changes++;
612                                         }
613                                 } else {
614                                         print ".\n";
615                                         $changes++;
616                                 }
617                         } else {
618                                 my $_assignments = $ASSIGNMENTS{$group};
619
620                                 foreach my $device (sort keys %{$_assignments}) {
621                                         if (!defined($used_assignments{$group}->{$device}) ||
622                                            ($$_assignments{$device} != $used_assignments{$group}->{$device})) {
623                                                 if ($$_assignments{$device} != $used_assignments{$group}->{$device}) {
624                                                         print "\t-> WARNING: Device '$device' assigned to group '$group' is at LUN ".
625                                                           $used_assignments{$group}->{$device}.
626                                                           " whereas working configuration reflects LUN ".$$_assignments{$device}; 
627                                                 } else {
628                                                         print "\t-> WARNING: Device '$device' is not associated with group ".
629                                                           "'$group' in saved configuration";
630                                                 }
631
632                                                 if (!$check) {
633                                                         print ", releasing.\n";
634                                                         if (releaseDevice($group, $device)) {
635                                                                 $errs++;
636                                                         } else {
637                                                                 $changes++;
638                                                         }
639                                                 } else {
640                                                         print ".\n";
641                                                         $changes++;
642                                                 }
643                                         }
644                                 }
645                         }
646                 }
647
648                 # Users & Groups
649                 foreach my $group (sort keys %USERS) {
650                         if (!defined($used_users{$group})) {
651                                 print "\t-> WARNING: Group '$group' does not exist in saved configuration";
652
653                                 if (!$check) {
654                                         print ", removing.\n";
655                                         if (clearUsers($group)) {
656                                                 $errs++;
657                                         } else {
658                                                 $changes++;
659                                         }
660
661                                         if (removeGroup($group)) {
662                                                 $errs++;
663                                         } else {
664                                                 $changes++;
665                                         }
666                                 } else {
667                                         print ".\n";
668                                         $changes++;
669                                 }
670                         } else {
671                                 foreach my $user (sort keys %{$USERS{$group}}) {
672                                         if (!defined($used_users{$group}->{$user})) {
673                                                 print "\t-> WARNING: User '$user' is not defined as part of group '$group' ".
674                                                   "in saved configuration";
675
676                                                 if (!$check) {
677                                                         print ", removing.\n";
678                                                         if (removeUser($group, $user)) {
679                                                                 $errs++;
680                                                         } else {
681                                                                 $changes++;
682                                                         }
683                                                 } else {
684                                                         print ".\n";
685                                                         $changes++;
686                                                 }
687                                         }
688                                 }
689                         }
690                 }
691
692                 # Devices
693                 foreach my $device (sort keys %{$DEVICES}) {
694                         if ($$DEVICES{$device} && !defined($used_devs{$device})) {
695                                 # Device gone, but is it still assigned to a group?
696                                 my $isAssigned = $FALSE;
697                                 foreach my $group (sort keys %used_assignments) {
698                                         if (defined($used_assignments{$group}->{$device})) {
699                                                 print "\t-> WARNING: Device '$device' is not defined in saved configuration, ".
700                                                   "however, it is still assigned to group '$group'! Ignoring removal.\n";
701                                                 $isAssigned = $TRUE;
702                                         }
703                                 }
704
705                                 if (!$isAssigned && ($SCST->handlerType($$DEVICES{$device}) == $SCST::SCST::IOTYPE_VIRTUAL)) {
706                                         print "\t-> WARNING: Device '$device' is not defined in saved configuration";
707
708                                         if (!$check) {
709                                                 print ", removing.\n";
710                                                 if (removeDevice($device)) {
711                                                         $errs++;
712                                                 } else {
713                                                         $changes++;
714                                                 }
715                                         } else {
716                                                 print ".\n";
717                                                 $changes++;
718                                         }
719                                 }
720                         } else {
721                                 # Handler change
722                                 if ($_HANDLER_MAP_{$used_devs{$device}} != $$DEVICES{$device}) {
723                                         my $handler = $used_devs{$device};
724
725                                         if ($HANDLERS{$_HANDLER_MAP_{$handler}}) {
726                                                 print "\t-> WARNING: Device '$device' changes handler to '$handler'";
727
728                                                 if (!$check) {
729                                                         print ", changing.\n";
730                                                         if ($SCST->assignDeviceToHandler($device,
731                                                                                          $_HANDLER_MAP_{$handler})) {
732                                                                 $errs++;
733                                                         } else {
734                                                                 $changes++;
735                                                         }
736                                                 } else {
737                                                         print ".\n";
738                                                         $changes++;
739                                                 }
740                                         }
741                                 }
742                         }
743                 }
744         }
745
746         print "Applying configurations additions..\n" if (!$check);
747         print "\n";
748
749         readWorkingConfig() if ($force);
750
751         foreach my $_handler (sort keys %{$$config{'HANDLER'}}) {
752                 if (!$HANDLERS{$_HANDLER_MAP_{$_handler}}) {
753                         print "\t-> WARNING: Handler '$_handler' does not exist.\n";
754                         $errs += 1;
755                         next;
756                 }
757
758                 foreach my $device (@{$$config{'HANDLER'}->{$_handler}->{'DEVICE'}}) {
759                         my($vname, $path, $options, $blocksize) = split(/\,/, $device);
760                         $path = cleanupString($path);
761                         $options =~ s/\s+//; $options =~ s/\|/,/;
762
763                         if (defined($$DEVICES{$vname}) && ($_HANDLER_MAP_{$_handler} == $$DEVICES{$vname})) {
764                                 next;
765                         } elsif (defined($$DEVICES{$vname}) && ($_HANDLER_MAP_{$_handler} != $$DEVICES{$vname})) {
766                                 if ($HANDLERS{$_HANDLER_MAP_{$_handler}}) {
767                                         print "\t-> WARNING: Device '$vname' changes handler from '".
768                                           $_REVERSE_MAP_{$$DEVICES{$vname}}."' to '$_handler'.\n".
769                                           "\t   Use -ForceConfig to change device handler.\n" if (!$force && !$check);
770                                 }
771                                 next;
772                         }
773
774                         if ($check) {
775                                 print "\t-> New device '$_handler:$vname' at path '$path', options '$options', ".
776                                   "blocksize $blocksize.\n";
777                                 $$DEVICES{$vname} = $_HANDLER_MAP_{$_handler};
778                                 $changes++;
779                         } else {
780                                 if (addDevice($_handler, $vname, $path, $options, $blocksize)) {
781                                         $errs++;
782                                 } else {
783                                         $changes++;
784                                 }
785                         }
786                 }
787         }
788
789         # Create new groups and add users..
790         foreach my $group (keys %used_users) {
791                 if (!defined($USERS{$group})) {
792                         if ($check) {
793                                 print "\t-> New group definition '$group.'\n";
794                                 $GROUPS{$group}++;
795                                 $changes++;
796                         } else {
797                                 if (addGroup($group)) {
798                                         $errs++;
799                                 } else {
800                                         $changes++;
801                                 }
802                         }
803                 }
804
805                 foreach my $user (keys %{$used_users{$group}}) {
806                         if (!defined($USERS{$group}->{$user})) {
807                                 if ($check) {
808                                         print "\t-> New user definition '$user' for group '$group'.\n";
809                                         $USERS{$group}->{$user}++;
810                                         $changes++;
811                                 } else {
812                                         if (addUser($group, $user)) {
813                                                 $errs++;
814                                         } else {
815                                                 $changes++;
816                                         }
817                                 }
818                         }
819                 }
820         }
821
822         # Assign new devices to groups..
823         foreach my $group (keys %used_assignments) {
824                 if (!defined($GROUPS{$group})) {
825                         print "\t-> WARNING: Unable to assign to non-existant group '$group'.\n";
826                         $errs += 1;
827                         next;
828                 }
829
830                 foreach my $vname (keys %{$used_assignments{$group}}) {
831                         my $lun = $used_assignments{$group}->{$vname};
832                         my $_assignments = $ASSIGNMENTS{$group};
833
834                         if (defined($$_assignments{$vname}) && ($$_assignments{$vname} == $lun)) {
835                                 next;
836                         } elsif (defined($$_assignments{$vname}) && ($$_assignments{$vname} != $lun)) {
837                                 print "\t-> Device '$vname' assigned to group '$group' is at LUN ".$$_assignments{$vname}.
838                                   ", whereas the working configuration reflects LUN $lun.\n".
839                                   "\t   Use -ForceConfig to force this LUN change.\n" if (!$force && !$check);
840                         } else {
841                                 if ($check) {
842                                         $lun = 'auto' if (!defined($lun));
843                                         print "\t-> New device assignment for '$vname' to group '$group' at LUN $lun.\n";
844                                         $changes++;
845                                 } else {
846                                         if (assignDevice($group, $vname, $lun)) {
847                                                 $errs++;
848                                         } else {
849                                                 $changes++;
850                                         }
851                                 }
852                         }
853                 }
854         }
855
856         # Enable/Disable configured targets
857         foreach my $type (keys %{$$config{'TARGETS'}}) {
858                 my $enable;
859
860                 if ($type eq 'enable') {
861                         $enable = $TRUE;
862                 } elsif ($type eq 'disable') {
863                         $enable = $FALSE;
864                 } else {
865                         print "\t-> WARNING: Ignoring invalid TARGETS specifier '$type'. ".
866                           "Should be one of enable,disable.\n";
867                         next;
868                 }
869
870                 foreach my $target (@{$$config{'TARGETS'}->{$type}->{'HOST'}}) {
871                         my $i_target = unformatTarget($target);
872
873                         if (!defined($$TARGETS{$i_target})) {
874                                 print "\t-> WARNING: Target '$target' not found on system.\n";
875                                 $errs += 1;
876                                 next;
877                         }
878
879                         next if ($enable == targetEnabled($i_target));
880
881                         if (!$enable && targetEnabled($target)) {
882                                 if ($force || $check) {
883                                         print "\t-> WARNING: Target mode for '$target' is currently enabled, ".
884                                           "however configuration file wants it disabled";
885
886                                         if (!$check) {
887                                                 print ", disabling.\n";
888                                                 if (enableTarget($target, $enable)) {
889                                                         $errs++;
890                                                 } else {
891                                                         $changes++;
892                                                 }
893                                         } else {
894                                                 print ".\n";
895                                                 $changes++;
896                                         }
897                                 }
898                         } else {
899                                 print "\t-> Target '$target' is enabled in configuration file, ".
900                                   "however is currently disabled";
901
902                                 if (!$check) {
903                                         print ", enabling.\n";
904                                         if (enableTarget($target, $enable)) {
905                                                 $errs++;
906                                         } else {
907                                                 $changes++;
908                                         }
909                                 } else {
910                                         print ".\n";
911                                         $changes++;
912                                 }
913                         }
914                 }
915         }
916
917         print "\nEncountered $errs error(s) while processing.\n" if ($errs);
918
919         if ($check) {
920                 print "Configuration checked, $changes difference(s) found with working configuration.\n";
921         } else {
922                 $changes = 0 if ($_DEBUG_);
923                 print "Configuration applied, $changes changes made.\n";
924         }
925
926         return $TRUE if ($errs);
927         return $FALSE;
928 }
929
930 sub clearConfiguration {
931         my $errs;
932
933         print "WARNING: This removes ALL applied SCST configuration and may result in data loss!\n";
934         print "If this is not what you intend, press ctrl-c now. Waiting 10 seconds.\n\n";
935         sleep 10;
936
937         print "\nRemoving all user and groups:\n\n";
938         foreach my $group (keys %GROUPS) {
939                 $errs += removeGroup($group) if ($group ne $_DEFAULT_GROUP_);
940         }
941
942         print "\nRemoving all handler devices:\n\n";
943         foreach my $device (keys %{$DEVICES}) {
944                 next if (!$$DEVICES{$device});
945                 next if ($SCST->handlerType($$DEVICES{$device}) != $SCST::SCST::IOTYPE_VIRTUAL);
946                 $errs += removeDevice($_REVERSE_MAP_{$$DEVICES{$device}}, $device);
947         }
948
949         print "\nEncountered $errs error(s) while processing.\n" if ($errs);
950         print "\nConfiguration cleared.\n";
951
952         return $TRUE if ($errs);
953         return $FALSE;
954 }
955
956 sub showSessions {
957         my $sessions = $SCST->sessions();
958         immediateExit($SCST->errorString());
959
960         print "\n\tTarget Name\tInitiator Name\t\t\tGroup Name\t\tCommand Count\n";
961
962         foreach my $target (keys %{$sessions}) {
963                 foreach my $group (keys %{$$sessions{$target}}) {
964                         foreach my $user (keys %{$$sessions{$target}->{$group}}) {
965                                 my $commands = $$sessions{$target}->{$group}->{$user};
966
967                                 print "\t$target\t$user\t\t$group\t\t$commands\n";
968                         }
969                 }
970         }
971
972         print "\n";
973
974         return $FALSE;
975 }
976
977 sub addDevice {
978         my $handler = shift;
979         my $device = shift;
980         my $path = shift;
981         my $options = shift;
982         my $blocksize = shift;
983
984         $options =~ s/\,/ /;
985
986         my $_handler = $_HANDLER_MAP_{$handler};
987         my $htype = $SCST->handlerType($_handler);
988
989         if (!$htype) {
990                 print "WARNING: Internal error occured: ".$SCST->errorString()."\n";
991                 return $TRUE;
992         }
993
994         if ($htype != $SCST::SCST::IOTYPE_VIRTUAL) {
995                 my $typeString = $_HANDLER_TYPE_MAP_{$htype};
996                 my $validType = $_HANDLER_TYPE_MAP_{$SCST::SCST::IOTYPE_VIRTUAL};
997                 print "WARNING: Handler $handler of type $typeString is incapable of ".
998                   "opening/closing devices. Valid handlers are:\n".
999                   validHandlerTypes($SCST::SCST::IOTYPE_VIRTUAL)."\n";
1000                 return $TRUE;
1001         }
1002
1003         if (defined($$DEVICES{$device})) {
1004                 print "WARNING: Device '$device' already defined.\n";
1005                 return $TRUE;
1006         }
1007
1008         print "\t-> Opening virtual device '$device' at path '$path' using handler '$handler'..\n";
1009
1010         if ($SCST->openDevice($_handler, $device, $path, $options, $blocksize)) {
1011                 print "WARNING: Failed to open virtual device '$device' at path '$path': ".
1012                   $SCST->errorString()."\n";
1013                 return $TRUE;
1014         }
1015
1016         $$DEVICES{$device} = $_handler;
1017
1018         return $FALSE;
1019 }
1020
1021 sub removeDevice {
1022         my $handler = shift;
1023         my $device = shift;
1024
1025         my $_handler = $_HANDLER_MAP_{$handler};
1026         my $htype = $SCST->handlerType($_handler);
1027
1028         if (!$htype) {
1029                 print "WARNING: Internal error occured: ".$SCST->errorString()."\n";
1030                 return $TRUE;
1031         }
1032
1033         if ($htype != $SCST::SCST::IOTYPE_VIRTUAL) {
1034                 my $typeString = $_HANDLER_TYPE_MAP_{$htype};
1035                 my $validType = $_HANDLER_TYPE_MAP_{$SCST::SCST::IOTYPE_VIRTUAL};
1036                 print "WARNING: Handler $handler of type $typeString is incapable of ".
1037                   "opening/closing devices. Valid handlers are:\n".
1038                   validHandlerTypes($SCST::SCST::IOTYPE_VIRTUAL)."\n";
1039                 return $TRUE;
1040         }
1041
1042         if (!defined($$DEVICES{$device})) {
1043                 print "WARNING: Device '$device' not defined.\n";
1044                 return $TRUE;
1045         }
1046
1047         print "\t-> Closing virtual device '$device'..\n";
1048
1049         if ($SCST->closeDevice($_handler, $device)) {
1050                 print "WARNING: Failed to close virtual device '$device': ".
1051                   $SCST->errorString()."\n";
1052                 return $TRUE;
1053         }
1054
1055         undef $$DEVICES{$device};
1056
1057         return $FALSE;
1058 }
1059
1060 sub addGroup {
1061         my $group = shift;
1062
1063         if (defined($GROUPS{$group})) {
1064                 print "WARNING: Group '$group' already exists.\n";
1065                 return $TRUE;
1066         }
1067
1068         print "\t-> Creating security group '$group'..\n";
1069
1070         if ($SCST->addGroup($group)) {
1071                 print "WARNING: Failed to create security group '$group': ".
1072                   $SCST->errorString()."\n";
1073                 return $TRUE;
1074         }
1075
1076         $GROUPS{$group}++;
1077
1078         return $FALSE;
1079 }
1080
1081 sub removeGroup {
1082         my $group = shift;
1083
1084         if (!defined($GROUPS{$group})) {
1085                 print "WARNING: Group '$group' does not exist.\n";
1086                 return $TRUE;
1087         }
1088
1089         print "\t-> Removing security group '$group'..\n";
1090
1091         if ($SCST->removeGroup($group)) {
1092                 print "WARNING: Failed to remove security group '$group': ".
1093                   $SCST->errorString()."\n";
1094                 return $TRUE;
1095         }
1096
1097         undef $GROUPS{$group};
1098
1099         return $FALSE;
1100 }
1101
1102 sub addUser {
1103         my $group = shift;
1104         my $user = shift;
1105
1106         if (!defined($GROUPS{$group})) {
1107                 print "WARNING: Failed to add user '$user' to group '$group', group does not exist.\n";
1108                 return $TRUE;
1109         }
1110
1111         if (defined($USERS{$group}->{$user})) {
1112                 print "WARNING: User '$user' already exists in security group '$group'.\n";
1113                 return $TRUE;
1114         }
1115
1116         print "\t-> Adding user '$user' to security group '$group'..\n";
1117
1118         if ($SCST->addUser($user, $group)) {
1119                 print "WARNING: Failed to add user '$user' to security group '$group': ".
1120                   $SCST->errorString()."\n";
1121                 return $TRUE;
1122         }
1123
1124         $USERS{$group}->{$user}++;
1125
1126         return $FALSE;
1127 }
1128
1129 sub removeUser {
1130         my $group = shift;
1131         my $user = shift;
1132
1133         if (!defined($GROUPS{$group})) {
1134                 print "WARNING: Failed to remove user '$user' from group '$group', group does not exist.\n";
1135                 return $TRUE;
1136         }
1137
1138         if (!defined($USERS{$group}->{$user})) {
1139                 print "WARNING: User '$user' doesn\'t exist in security group '$group'.\n";
1140                 return $TRUE;
1141         }
1142
1143         print "\t-> Removing user '$user' from security group '$group'..\n";
1144
1145         if ($SCST->removeUser($user, $group)) {
1146                 print "WARNING: Failed to add user '$user' to security group '$group': ".
1147                   $SCST->errorString()."\n";
1148                 return $TRUE;
1149         }
1150
1151         undef $USERS{$group}->{$user};
1152
1153         return $FALSE;
1154 }
1155
1156 sub clearUsers {
1157         my $group = shift;
1158
1159         if (!defined($GROUPS{$group})) {
1160                 print "WARNING: Failed to clear users from group '$group', group does not exist.\n";
1161                 return $TRUE;
1162         }
1163
1164         print "\t-> Clearing users from security group '$group'..\n";
1165
1166         if ($SCST->clearUsers($group)) {
1167                 print "WARNING: Failed to clear users from security group '$group': ".
1168                   $SCST->errorString()."\n";
1169                 return $TRUE;
1170         }
1171
1172         undef $USERS{$group};
1173
1174         return $FALSE;
1175 }
1176
1177 sub assignDevice {
1178         my $group = shift;
1179         my $device = shift;
1180         my $lun = shift;
1181         my %allLuns;
1182
1183         # Put luns into something easier to parse..
1184         foreach my $_group (keys %ASSIGNMENTS) {
1185                 my $_gAssigns = $ASSIGNMENTS{$_group};
1186
1187                 foreach my $_device (keys %{$_gAssigns}) {
1188                         @{$allLuns{$_group}}[$$_gAssigns{$_device}] = $_device;
1189                 }
1190         }
1191
1192         # Use the next available LUN if none specified
1193         if ($lun !~ /\d+/) {
1194                 $lun = ($#{$allLuns{$group}} + 1);
1195                 if ($lun > $_MAX_LUNS_) {
1196                         print "ERROR: Unable to use next available LUN of $lun, lun out of range.\n";
1197                         return $TRUE;
1198                 }
1199
1200                 print "\t-> Device '$device': Using next available LUN of $lun for group '$group'.\n";
1201         }
1202                         
1203         if (($lun < 0) || ($lun > $_MAX_LUNS_)) {
1204                 print "ERROR: Unable to assign device '$device', lun '$lun' is out of range.\n";
1205                 return $TRUE;
1206         }
1207
1208         if (!defined($$DEVICES{$device})) {
1209                 print "WARNING: Unable to assign non-existant device '$device' to group '$group'.\n";
1210                 return $TRUE;
1211         }
1212
1213         if (@{$allLuns{$group}}[$lun]) {
1214                 print "ERROR: Device '$device': Lun '$lun' is already assigned to device '".@{$allLuns{$group}}[$lun]."'.\n";
1215                 return $TRUE;
1216         }
1217
1218         print "\t-> Assign virtual device '$device' to group '$group' at LUN '$lun'..\n";
1219
1220         if ($SCST->assignDeviceToGroup($device, $group, $lun)) {
1221                 print "WARNING: Failed to assign device '$device' to group '$group': ".
1222                   $SCST->errorString()."\n";
1223                 return $TRUE;
1224         }
1225
1226         if (!defined($ASSIGNMENTS{$group})) {
1227                 my %assignments_t;
1228                 $ASSIGNMENTS{$group} = \%assignments_t;
1229         }
1230
1231         my $_assignments = $ASSIGNMENTS{$group};
1232
1233         $$_assignments{$device} = $lun; 
1234
1235         return $FALSE;
1236 }
1237
1238 sub releaseDevice {
1239         my $group = shift;
1240         my $device = shift;
1241
1242         if (!defined($GROUPS{$group})) {
1243                 print "WARNING: Failed to release device '$device' from group '$group', group does not exist.\n";
1244                 return $TRUE;
1245         }
1246
1247         if (!defined($$DEVICES{$device})) {
1248                 print "WARNING: Failed to release device '$device', device not defined.\n";
1249                 return $TRUE;
1250         }
1251
1252         print "\t-> Release virtual device '$device' from group '$group'..\n";
1253
1254         if ($SCST->removeDeviceFromGroup($device, $group)) {
1255                 print "WARNING: Failed to release device '$device' from group '$group': ".
1256                   $SCST->errorString()."\n";
1257                 return $TRUE;
1258         }
1259
1260         my $_assignments = $ASSIGNMENTS{$group};
1261
1262         undef $$_assignments{$device};
1263
1264         return $FALSE;
1265 }
1266
1267 sub clearDevices {
1268         my $group = shift;
1269
1270         if (!defined($GROUPS{$group})) {
1271                 print "WARNING: Failed to clear devices from group '$group', group does not exist.\n";
1272                 return $TRUE;
1273         }
1274
1275         print "\t-> Clear virtual devices from group '$group'..\n";
1276
1277         if ($SCST->clearGroupDevices($group)) {
1278                 print "WARNING: Failed to clear devices from group '$group': ".
1279                   $SCST->errorString()."\n";
1280                 return $TRUE;
1281         }
1282
1283         undef $ASSIGNMENTS{$group};
1284
1285         return $FALSE;
1286 }
1287
1288 sub targets {
1289         my %targets;
1290         my %fcards;
1291         my $root = new IO::Dir $_FC_CLASS_;
1292
1293         if ($root) {
1294                 while (my $entry = $root->read()) {
1295                         next if (($entry eq '.') || ($entry eq '..'));
1296
1297                         my $io = new IO::File "$_FC_CLASS_/$entry/port_name", O_RDONLY;
1298
1299                         if ($io) {
1300                                 my $wwn = <$io>;
1301                                 chomp $wwn;
1302                                 close $io;
1303
1304                                 $fcards{$entry} = $wwn;
1305                         }
1306                 }
1307         }
1308
1309         $root = new IO::Dir $_SCSI_CLASS_;
1310
1311         if ($root) {
1312                 while (my $entry = $root->read()) {
1313                         next if (($entry eq '.') || ($entry eq '..'));
1314
1315                         my $io = new IO::File "$_SCSI_CLASS_/$entry/target_mode_enabled", O_RDONLY;
1316
1317                         if ($io) {
1318                                 my $enabled = <$io>;
1319                                 chomp $enabled;
1320                                 close $io;
1321
1322                                 $targets{$entry}->{'path'} = "$_SCSI_CLASS_/$entry/target_mode_enabled";
1323                                 $targets{$entry}->{'enabled'} = $enabled;
1324
1325                                 if ($fcards{$entry}) {
1326                                         $targets{$fcards{$entry}}->{'enabled'} = $enabled;
1327                                         $targets{$fcards{$entry}}->{'path'} =
1328                                           "$_SCSI_CLASS_/$entry/target_mode_enabled";
1329                                         $targets{$entry}->{'duplicate'} = $TRUE;
1330                                 } else {
1331                                         $targets{$entry}->{'duplicate'} = $FALSE;
1332                                 }
1333                         }
1334                 }
1335         }
1336
1337         return \%targets;
1338 }
1339
1340 sub targetEnabled {
1341         my $target = shift;
1342
1343         return undef if (!defined($$TARGETS{$target}));
1344         return $$TARGETS{$target}->{'enabled'};
1345 }
1346
1347 sub enableTarget {
1348         my $target = shift;
1349         my $enable = shift;
1350
1351         return undef if (!defined($$TARGETS{$target}));
1352
1353         my $io = new IO::File $$TARGETS{$target}->{'path'}, O_WRONLY;
1354         return $TRUE if (!$io);
1355
1356         print $enable ? "\t-> Enabling" : "\t-> Disabling";
1357         print " target mode for SCST host '$target'.\n";
1358
1359         if ($_DEBUG_) {
1360                 print "DBG($$): ".$$TARGETS{$target}->{'path'}." -> $enable\n\n";
1361         } else {
1362                 print $io $enable;
1363         }
1364
1365         close $io;
1366
1367         $$TARGETS{unformatTarget($target)}->{'enabled'} = $enable;
1368
1369         return $FALSE;
1370 }
1371
1372 sub readConfig {
1373         my $confile = shift;
1374         my %config;
1375         my $section;
1376         my $arg;
1377
1378         my $io = new IO::File $confile, O_RDONLY;
1379
1380         die("FATAL: Unable to open specified configuration file $confile: $!\n") if (!$io);
1381
1382         while (my $line = <$io>) {
1383                 ($line, undef) = split(/\#/, $line, 2);
1384                 $line = cleanupString($line);
1385
1386                 if ($line =~ /^\[(.*)\]$/) {
1387                         ($section, $arg) = split(/\s+/, $1, 2);
1388                 } elsif ($section && $arg && $line) {
1389                         my($parameter, $value) = split(/\s+/, $line, 2);
1390
1391                         push @{$config{$section}->{$arg}->{$parameter}}, $value;
1392                 }
1393         }
1394
1395         close $io;
1396
1397         return \%config;
1398 }
1399
1400 sub cleanupString {
1401         my $string = shift;
1402
1403         $string =~ s/^\s+//;
1404         $string =~ s/\s+$//;
1405
1406         return $string;
1407 }
1408
1409 sub formatTarget {
1410         my $target = shift;
1411
1412         if ($target =~ /^0x/) {
1413                 $target =~ s/^0x//;
1414                 my($o1, $o2, $o3, $o4, $o5, $o6, $o7, $o8) = unpack("A2A2A2A2A2A2A2A2", $target);
1415                 $target = "$o1:$o2:$o3:$o4:$o5:$o6:$o7:$o8";
1416         }
1417
1418         $target =~ tr/A-Z/a-z/;
1419
1420         return $target;
1421 }
1422
1423 sub unformatTarget {
1424         my $target = shift;
1425
1426         if ($target =~ /^.{2}\:.{2}\:.{2}\:.{2}\:.{2}\:.{2}\:.{2}\:.{2}/) {
1427                 $target =~ s/\://g;
1428                 $target = "0x$target";
1429         }
1430
1431         $target =~ tr/A-Z/a-z/;
1432
1433         return $target;
1434 }
1435
1436 sub validHandlerTypes {
1437         my $type = shift;
1438         my $buffer = "\n";
1439
1440         foreach my $handler (keys %_REVERSE_MAP_) {
1441                 $buffer .= "\t".$_REVERSE_MAP_{$handler}."\n" if ($SCST->handlerType($handler) == $type);
1442         }
1443
1444         return $buffer;
1445 }
1446
1447 # If we have an unread error from SCST, exit immediately
1448 sub immediateExit {
1449         my $error = shift;
1450
1451         return if (!$error);
1452
1453         print "\n\nFATAL: Received the following error:\n\n\t";
1454         print "$error\n\n";
1455
1456         exit 1;
1457 }
1458
1459 # Hey! Stop that!
1460 sub commitSuicide {
1461         print "\n\nAborting immediately.\n";
1462         exit 1;
1463 }