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