A bunch of small fixes and cleanups before 0.9.5
[mirror/scst/.git] / scstadmin / scstadmin
1 #!/usr/bin/perl
2 $Version  = 'SCST Configurator v0.6';
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, disk_fileio, disk_perf, cdrom, 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
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 disk_fileio -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                      'disk_fileio' => $SCST::SCST::DISKFILE_TYPE,
117                      'cdrom_fileio' => $SCST::SCST::CDROMFILE_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                      
124 my %_REVERSE_MAP_ = ($SCST::SCST::CDROM_TYPE => 'cdrom',
125                      $SCST::SCST::CHANGER_TYPE => 'changer',
126                      $SCST::SCST::DISK_TYPE => 'disk',
127                      $SCST::SCST::DISKFILE_TYPE => 'disk_fileio',
128                      $SCST::SCST::CDROMFILE_TYPE => 'cdrom_fileio',
129                      $SCST::SCST::DISKPERF_TYPE => 'disk_perf',
130                      $SCST::SCST::MODISK_TYPE => 'modisk',
131                      $SCST::SCST::MODISKPERF_TYPE => 'modisk_perf',
132                      $SCST::SCST::TAPE_TYPE => 'tape',
133                      $SCST::SCST::TAPEPERF_TYPE => 'tape_perf');
134
135 $SIG{INT} = \&commitSuicide;
136
137 POSIX::setsid();
138
139 &main();
140
141 sub getArgs {
142         my $applyConfig;
143         my $forceConfig;
144         my $clearConfig;
145         my $writeConfig;
146         my $checkConfig;
147         my $showSessions;
148         my $addDev;
149         my $devPath;
150         my $removeDev;
151         my $addUser;
152         my $removeUser;
153         my $clearUsers;
154         my $addGroup;
155         my $removeGroup;
156         my $assignDev;
157         my $releaseDev;
158         my $clearDevs;
159         my $devLun;
160         my $handler;
161         my $group;
162         my $options;
163         my $blocksize;
164         my $enable;
165         my $disable;
166
167         my $p = new Getopt::Long::Parser;
168
169         if (!$p->getoptions('config:s'          => \$applyConfig,
170                             'ClearConfig'       => \$clearConfig,
171                             'ForceConfig'       => \$forceConfig,
172                             'WriteConfig=s'     => \$writeConfig,
173                             'checkConfig=s'     => \$checkConfig,
174                             'sessions'          => \$showSessions,
175                             'adddev=s'          => \$addDev,
176                             'path=s'            => \$devPath,
177                             'RemoveDev=s'       => \$removeDev,
178                             'lun=s'             => \$devLun,
179                             'adduser=s'         => \$addUser,
180                             'RemoveUser=s'      => \$removeUser,
181                             'ClearUsers'        => \$clearUsers,
182                             'addgroup=s'        => \$addGroup,
183                             'RemoveGroup=s'     => \$removeGroup,
184                             'assigndev=s'       => \$assignDev,
185                             'ReleaseDev=s'      => \$releaseDev,
186                             'ClearDevs'         => \$clearDevs,
187                             'handler=s'         => \$handler,
188                             'group=s'           => \$group,
189                             'options=s'         => \$options,
190                             'blocksize=s'       => \$blocksize,
191                             'enable=s'          => \$enable,
192                             'disable=s'         => \$disable,
193                             'debug'             => \$_DEBUG_)) {
194                 &usage();
195         }
196
197         if ((defined($enable) && !$enable) || (defined($disable) && !$disable)) {
198                 print "Argument -enable/-disable requires a WWN or host.\n\n";
199                 usage();
200         }
201
202         if ($handler && !$_HANDLER_MAP_{$handler}) {
203                 print "Invalid handler '$handler' specified. Available handlers are:\n\n";
204                 foreach my $_handler (keys %_HANDLER_MAP_) {
205                         print "\t$_handler\n";
206                 }
207                 print "\n";
208                 exit 1;
209         }
210
211         if ($addDev && !($handler && $devPath)) {
212                 print "Please specify -handler and -path with -adddev.\n\n";
213                 usage();
214         }
215
216         if (defined($blocksize) && !$blocksize) {
217                 print "Please specify bytes with -blocksize.\n\n";
218                 usage();
219         }
220
221         if ($blocksize && !$addDev) {
222                 print "Please specify -adddev with -blocksize.\n";
223                 usage();
224         }
225
226         if (defined($forceConfig) && !defined($applyConfig)) {
227                 print "Please specify -config with -ForceConfig.\n\n";
228                 usage();
229         }
230
231         if ($removeDev && !$handler) {
232                 print "Please specify -handler with -RemoveDev.\n\n";
233                 usage();
234         }
235
236         if ($addUser && !defined($group)) {
237                 print "Please specify -group with -adduser.\n\n";
238                 usage();
239         }
240
241         if ($removeUser && !defined($group)) {
242                 print "Please specify -group with -RemoveUser.\n\n";
243                 usage();
244         }
245
246         if ($clearUsers && !defined($group)) {
247                 print "Please specify -group with -ClearUsers.\n\n";
248                 usage();
249         }
250
251         if ($assignDev && !(defined($group) && defined($devLun))) {
252                 print "Please specify -group and -lun with -assigndev.\n\n";
253                 usage();
254         }
255
256         if ($releaseDev && !defined($group)) {
257                 print "Please specify -group with -RemoveDev.\n\n";
258                 usage();
259         }
260
261         if ($clearDevs && !defined($group)) {
262                 print "Please specify -group with -ClearDevs.\n\n";
263                 usage();
264         }
265
266         if (defined($writeConfig) && !$writeConfig) {
267                 print "Please specify a file name to write configuration to..\n\n";
268                 usage();
269         }
270
271         $forceConfig = $TRUE if (defined($forceConfig));
272         $showSessions = $TRUE if (defined($showSessions));
273
274         $enable =~ tr/A-Z/a-z/; $disable =~ tr/A-Z/a-z/;
275
276         if ((defined($showSessions) + defined($addDev) + defined($removeDev) +
277              defined($addUser) + defined($enable) + defined($disable) +
278              defined($removeUser) + defined($clearUsers) + defined($assignDev) +
279              defined($releaseDev) + defined($clearDevs) + defined($applyConfig) +
280              defined($clearConfig) + defined($writeConfig) + defined($checkConfig)) > 1) {
281                 print "Please specify only one operation at a time.\n";
282                 usage();
283         }
284
285         $applyConfig = $_DEF_CONFIG_ if (defined($applyConfig) && !$applyConfig);
286         $checkConfig = $_DEF_CONFIG_ if (defined($checkConfig) && !$checkConfig);
287
288         return ($enable, $disable, $addDev, $devPath, $devLun, $removeDev, $addUser, $removeUser,
289                 $clearUsers, $addGroup, $removeGroup, $assignDev, $releaseDev, $clearDevs,
290                 $handler, $group, $options, $blocksize, $applyConfig, $forceConfig,
291                 $clearConfig, $writeConfig, $checkConfig, $showSessions);
292 }
293
294 sub main {
295         my $rc;
296
297         STDOUT->autoflush(1);
298
299         # We need to run as root
300         if ( $> ) {die("This program must run as root.\n");}
301
302         my ($enable, $disable, $addDev, $devPath, $devLun, $removeDev, $addUser, $removeUser,
303             $clearUsers, $addGroup, $removeGroup, $assignDev, $releaseDev, $clearDevs,
304             $handler, $group, $options, $blocksize, $applyConfig, $forceConfig,
305             $clearConfig, $writeConfig, $checkConfig, $showSessions) = getArgs();
306
307         $SCST = new SCST::SCST($_DEBUG_);
308
309         readCurrentConfig();
310
311         SWITCH: {
312                 $applyConfig && do {
313                         if ($forceConfig) {
314                                 $rc = applyConfiguration($applyConfig, $FALSE, $TRUE);
315                                 die("Configuration errors found, aborting.\n") if ($rc);
316
317                                 print "\nConfiguration will apply in 10 seconds, type ctrl-c to abort..\n";
318                                 sleep 10;
319                         }
320
321                         $rc = applyConfiguration($applyConfig, $forceConfig, $FALSE);
322                         last SWITCH;
323                 };
324                 $checkConfig && do {
325                         $rc = applyConfiguration($checkConfig, $FALSE, $TRUE);
326                         last SWITCH;
327                 };
328                 $writeConfig && do {
329                         $rc = writeConfiguration($writeConfig);
330                         last SWITCH;
331                 };
332                 $showSessions && do {
333                         $rc = showSessions();
334                         last SWITCH;
335                 };
336                 defined($clearConfig) && do {
337                         $rc = clearConfiguration();
338                         last SWITCH;
339                 };
340                 $addDev && do {
341                         $rc = addDevice($handler, $addDev, $devPath, $options, $blocksize);
342                         last SWITCH;
343                 };
344                 $removeDev && do {
345                         $rc = removeDevice($handler, $removeDev);
346                         last SWITCH;
347                 };
348                 $addUser && do {
349                         $rc = addUser($group, $addUser);
350                         last SWITCH;
351                 };
352                 $removeUser && do {
353                         $rc = removeUser($group, $removeUser);
354                         last SWITCH;
355                 };
356                 defined($clearUsers) && do {
357                         $rc = clearUsers($group);
358                         last SWITCH;
359                 };
360                 $addGroup && do {
361                         $rc = addGroup($addGroup);
362                         last SWITCH;
363                 };
364                 $removeGroup && do {
365                         $rc = removeGroup($removeGroup);
366                         last SWITCH;
367                 };
368                 $assignDev && do {
369                         $rc = assignDevice($group, $assignDev, $devLun);
370                         last SWITCH;
371                 };
372                 $releaseDev && do {
373                         $rc = releaseDevice($group, $releaseDev);
374                         last SWITCH;
375                 };
376                 defined($clearDevs) && do {
377                         $rc = clearDevices($group);
378                         last SWITCH;
379                 };
380                 $enable && do {
381                         $enable = unformatTarget($enable);
382                         $rc = enableTarget($enable, $TRUE);
383                         last SWITCH;
384                 };
385                 $disable && do {
386                         $disable = unformatTarget($disable);
387                         $rc = enableTarget($disable, $FALSE);
388                         last SWITCH;
389                 };
390
391                 print "No valid operations specified.\n";
392                 usage();
393                 exit $TRUE;
394         }
395
396         print "All done.\n";
397
398         exit $rc;
399 }
400
401 sub readCurrentConfig {
402         print "Collecting current configuration.. ";
403
404         my $eHandlers = $SCST->handlers();
405
406         foreach my $handler (@{$eHandlers}) {
407                 $HANDLERS{$handler}++; # For quick lookups
408         }
409
410         $TARGETS = targets();
411
412         $DEVICES = $SCST->devices();
413         my $_eGroups = $SCST->groups();
414
415         foreach my $group (@{$_eGroups}) {
416                 $GROUPS{$group}++; # For quick lookups
417                 $ASSIGNMENTS{$group} = $SCST->groupDevices($group);
418                 my $eUsers = $SCST->users($group);
419
420                 foreach my $user (@{$eUsers}) {
421                         $USERS{$group}->{$user}++; # For quick lookups
422                 }
423         }
424
425         print "done.\n";
426 }
427
428 sub writeConfiguration {
429         my $file = shift;
430
431         if (-f $file) {
432                 if (!unlink $file) {
433                         print "Failed to save current configuration, specified ".
434                           "file exists and cannot be deleted.\n";
435                         return 1;
436                 }
437         }
438
439         my $io = new IO::File $file, O_CREAT|O_WRONLY;
440
441         if (!$io) {
442                 print "Failed to save configuration to file '$file': $!\n";
443                 return 1;
444         }
445
446         print "Writing current configuration to file '$file'.. ";
447
448         print $io "# Automatically generated by scst.\n\n";
449
450         # Device information
451         foreach my $handler (sort keys %HANDLERS) {
452                 print $io "[HANDLER ".$_REVERSE_MAP_{$handler}."]\n";
453
454                 my $devices = $SCST->handlerDevices($handler);
455
456                 foreach my $device (sort keys %{$devices}) {
457                         my $options = $$devices{$device}->{'OPTIONS'};
458                         $options =~ s/\,/\|/;
459
460                         print $io "DEVICE $device,".$$devices{$device}->{'PATH'};
461                         print $io ",$options";
462                         print $io ",".$$devices{$device}->{'BLOCKSIZE'};
463                         print $io "\n";
464                 }
465
466                 print $io "\n";
467         }
468
469         # User configuration
470         foreach my $group (sort keys %USERS) {
471                 print $io "[GROUP $group]\n";
472
473                 foreach my $user (keys %{$USERS{$group}}) {
474                         print $io "USER $user\n";
475                 }
476
477                 print $io "\n";
478         }
479
480         # Assignments configuration
481         foreach my $group (sort keys %ASSIGNMENTS) {
482                 print $io "[ASSIGNMENT $group]\n";
483
484                 my $pointer = $ASSIGNMENTS{$group};
485                 foreach my $device (sort keys %{$pointer}) {
486                         print $io "DEVICE $device,".$$pointer{$device}."\n";
487                 }
488
489                 print $io "\n";
490         }
491
492         # Targets configuration
493         foreach my $type ('enable', 'disable') {
494                 print $io "[TARGETS $type]\n";
495
496                 foreach my $target (sort keys %{$TARGETS}) {
497                         if ((($type eq 'enable') && $$TARGETS{$target}->{'enabled'}) ||
498                             (($type eq 'disable') && !$$TARGETS{$target}->{'enabled'})) {
499                                 my $f_target = formatTarget($target);
500                                 print $io "HOST $f_target\n" if (!$$TARGETS{$target}->{'duplicate'});
501                         }
502                 }
503
504                 print $io "\n";
505         }
506
507         print "done\n";
508
509         close $io;
510
511         return 0;
512 }
513
514 sub applyConfiguration {
515         my $confile = shift;
516         my $force = shift;
517         my $check = shift;
518         my $config = readConfig($confile);
519         my $errs;
520         my $changes = 0;
521
522         my %used_devs;
523         my %used_users;
524         my %used_assignments;
525
526         print "Applying configurations additions..\n" if (!$check);
527         print "\n";
528
529         # Enable/Disable configured targets
530         foreach my $type (keys %{$$config{'TARGETS'}}) {
531                 my $enable;
532
533                 if ($type eq 'enable') {
534                         $enable = $TRUE;
535                 } elsif ($type eq 'disable') {
536                         $enable = $FALSE;
537                 } else {
538                         print "\t-> WARNING: Ignoring invalid TARGETS specifier '$type'. ".
539                           "Should be one of enable,disable.\n";
540                         next;
541                 }
542
543                 foreach my $target (@{$$config{'TARGETS'}->{$type}->{'HOST'}}) {
544                         my $i_target = unformatTarget($target);
545
546                         if (!defined($$TARGETS{$i_target})) {
547                                 print "\t-> WARNING: Target '$target' not found on system.\n";
548                                 $errs += 1;
549                                 next;
550                         }
551
552                         next if ($enable == targetEnabled($i_target));
553
554                         if (!$enable && targetEnabled($target)) {
555                                 if ($force || $check) {
556                                         print "\t-force: Target mode for '$target' is currently enabled, ".
557                                           "however configuration file wants it disabled";
558
559                                         if (!$check) {
560                                                 print ", disabling.\n";
561                                                 $errs += enableTarget($target, $enable);
562                                         } else {
563                                                 print ".\n";
564                                         }
565
566                                         $changes++;
567                                 }
568                         } else {
569                                 print "\t-> Target '$target' is enabled in configuration file, ".
570                                   "however is currently disabled";
571
572                                 if (!$check) {
573                                         print ", enabling.\n";
574                                         $errs += enableTarget($target, $enable);
575                                 } else {
576                                         print ".\n";
577                                 }
578
579                                 $changes++;
580                         }
581                 }
582         }
583
584         # Open new devices and assign them to handlers..
585         foreach my $entry (keys %{$$config{'HANDLER'}}) {
586                 if (!$HANDLERS{$_HANDLER_MAP_{$entry}}) {
587                         print "\t-> WARNING: Handler '$entry' does not exist.\n";
588                         $errs += 1;
589                         next;
590                 }
591
592                 foreach my $device (@{$$config{'HANDLER'}->{$entry}->{'DEVICE'}}) {
593                         my($vname, $path, $options, $blocksize) = split(/\,/, $device);
594                         $vname = cleanupString($vname);
595                         $path = cleanupString($path);
596
597                         $options =~ s/\s+//; $options =~ s/\|/,/;
598
599                         $used_devs{$vname} = $entry;
600
601                         next if (defined($$DEVICES{$vname}));
602
603                         $changes++;
604
605                         if ($check) {
606                                 print "\t-> New device '$entry:$vname' at path '$path', options '$options', blocksize $blocksize.\n";
607                                 $$DEVICES{$vname} = $_HANDLER_MAP_{$entry};
608                         } else {
609                                 $errs += addDevice($entry, $vname, $path, $options, $blocksize);
610                         }
611                 }
612         }
613
614         # Create new groups and add users..
615         foreach my $group (keys %{$$config{'GROUP'}}) {
616                 if (!defined($GROUPS{$group})) {
617                         $changes++;
618
619                         if ($check) {
620                                 print "\t-> New group definition '$group.'\n";
621                                 $GROUPS{$group}++;
622                         } else {
623                                 $errs += addGroup($group);
624                         }
625                 }
626
627                 foreach my $user (@{$$config{'GROUP'}->{$group}->{'USER'}}) {
628                         $used_users{$group}->{$user}++;
629
630                         if (!defined($USERS{$group}->{$user})) {
631                                 $changes++;
632
633                                 if ($check) {
634                                         print "\t-> New user definition '$user' for group '$group'.\n";
635                                         $USERS{$group}->{$user}++;
636                                 } else {
637                                         $errs += addUser($group, $user);
638                                 }
639                         }
640                 }
641         }
642
643         # Assign new devices to groups..
644         foreach my $group (keys %{$$config{'ASSIGNMENT'}}) {
645                 if (!defined($GROUPS{$group})) {
646                         print "\t-> WARNING: Unable to assign to non-existant group '$group'.\n";
647                         $errs += 1;
648                         next;
649                 }
650
651                 foreach my $device (@{$$config{'ASSIGNMENT'}->{$group}->{'DEVICE'}}) {
652                         my($vname, $lun) = split(/\,/, $device);
653                         $vname = cleanupString($vname);
654                         $lun = cleanupString($lun);
655
656                         $used_assignments{$group}->{$vname}++;
657
658                         my $_assignments = $ASSIGNMENTS{$group};
659                         next if (defined($$_assignments{$vname}));
660
661                         $changes++;
662
663                         if ($check) {
664                                 $lun = 'auto' if (!defined($lun));
665                                 print "\t-> New device assignment for '$vname' to group '$group' at LUN $lun.\n";
666                         } else {
667                                 $errs += assignDevice($group, $vname, $lun);
668                         }
669                 }
670         }
671
672         # If -ForceConfig is used, check for configurations which we've deleted but are still active.
673         if ($force || $check) {
674                 readCurrentConfig() if (!$check);
675
676                 # Associations
677                 foreach my $group (sort keys %ASSIGNMENTS) {
678                         if (!defined($used_assignments{$group})) {
679                                 print "\t-force: Group $group has no associations in saved configuration";
680
681                                 if (!$check) {
682                                         print ", clearing all associations.\n";
683                                         $errs += clearDevices($group);
684                                 } else {
685                                         print ".\n";
686                                 }
687
688                                 $changes++;
689                         } else {
690                                 my $_assignments = $ASSIGNMENTS{$group};
691
692                                 foreach my $device (sort keys %{$_assignments}) {
693                                         if (!defined($used_assignments{$group}->{$device})) {
694                                                 print "\t-force: Device $device is not associated with group ".
695                                                   "$group in saved configuration";
696
697                                                 if (!$check) {
698                                                         print ", releasing.\n";
699                                                         $errs += releaseDevice($group, $device);
700                                                 } else {
701                                                         print ".\n";
702                                                 }
703
704                                                 $changes++;
705                                         }
706                                 }
707                         }
708                 }
709
710                 # Users & Groups
711                 foreach my $group (sort keys %USERS) {
712                         if (!defined($used_users{$group})) {
713                                 print "\t-force: Group $group does not exist in saved configuration";
714
715                                 if (!$check) {
716                                         print ", removing.\n";
717                                         $errs += clearUsers($group);
718                                         $errs += removeGroup($group);
719                                 } else {
720                                         print ".\n";
721                                 }
722
723                                 $changes++;
724                         } else {
725                                 foreach my $user (sort keys %{$USERS{$group}}) {
726                                         if (!defined($used_users{$group}->{$user})) {
727                                                 print "\t-force: User $user is not defined as part of group $group ".
728                                                   "in saved configuration";
729
730                                                 if (!$check) {
731                                                         print ", removing.\n";
732                                                         $errs += removeUser($group, $user);
733                                                 } else {
734                                                         print ".\n";
735                                                 }
736
737                                                 $changes++;
738                                         }
739                                 }
740                         }
741                 }
742
743                 # Devices
744                 foreach my $device (sort keys %{$DEVICES}) {
745                         if ($$DEVICES{$device} && !defined($used_devs{$device})) {
746                                 # Device gone, but is it still assigned tp a group?
747                                 my $isAssigned = $FALSE;
748                                 foreach my $group (sort keys %used_assignments) {
749                                         if (defined($used_assignments{$group}->{$device})) {
750                                                 print "\t-force: WARNING: Device $device is not defined in saved configuration, ".
751                                                   "however, it is still assigned to group $group! Ignoring removal.\n";
752                                                 $isAssigned = $TRUE;
753                                         }
754                                 }
755
756                                 if (!$isAssigned) {
757                                         print "\t-force: Device $device is not defined in saved configuration";
758
759                                         if (!$check) {
760                                                 print ", removing.\n";
761                                                 $errs += removeDevice($device);
762                                         } else {
763                                                 print ".\n";
764                                         }
765
766                                         $changes++;
767                                 }
768                         } else {
769                                 # Handler change
770                                 if ($_HANDLER_MAP_{$used_devs{$device}} != $$DEVICES{$device}) {
771                                         my $handler = $used_devs{$device};
772
773                                         if ($HANDLERS{$_HANDLER_MAP_{$handler}}) {
774                                                 print "\t-force: Device $device changes handler to $handler";
775
776                                                 if (!$check) {
777                                                         print ", changing.\n";
778                                                         $errs += assignDeviceToHandler($device, $handler);
779
780                                                 } else {
781                                                         print ".\n";
782                                                 }
783
784                                                 $changes++;
785                                         }
786                                 }
787                         }
788                 }
789         }
790
791         print "\nEncountered $errs error(s) while processing.\n" if ($errs);
792
793         if ($check) {
794                 print "Configuration checked, $changes difference(s) found with current configuration.\n";
795         } else {
796                 $changes = 0 if ($_DEBUG_);
797                 print "Configuration applied, $changes changes made.\n";
798         }
799
800         return $TRUE if ($errs);
801         return $FALSE;
802 }
803
804 sub clearConfiguration {
805         my $errs;
806
807         print "WARNING: This removes ALL applied SCST configuration and may result in data loss!\n";
808         print "If this is not what you intend, press ctrl-c now. Waiting 10 seconds.\n\n";
809         sleep 10;
810
811         print "\nRemoving all user and groups:\n\n";
812         foreach my $group (keys %GROUPS) {
813                 $errs += removeGroup($group) if ($group ne $_DEFAULT_GROUP_);
814         }
815
816         print "\nRemoving all handler devices:\n\n";
817         foreach my $device (keys %{$DEVICES}) {
818                 $errs += removeDevice($_REVERSE_MAP_{$$DEVICES{$device}}, $device);
819         }
820
821         print "\nEncountered $errs error(s) while processing.\n" if ($errs);
822         print "\nConfiguration cleared.\n";
823
824         return $TRUE if ($errs);
825         return $FALSE;
826 }
827
828 sub showSessions {
829         my $sessions = $SCST->sessions();
830
831         print "\n\tTarget Name\tInitiator Name\t\t\tGroup Name\t\tCommand Count\n";
832
833         foreach my $target (keys %{$sessions}) {
834                 foreach my $group (keys %{$$sessions{$target}}) {
835                         foreach my $user (keys %{$$sessions{$target}->{$group}}) {
836                                 my $commands = $$sessions{$target}->{$group}->{$user};
837
838                                 print "\t$target\t$user\t\t$group\t\t$commands\n";
839                         }
840                 }
841         }
842
843         print "\n";
844
845         return $FALSE;
846 }
847
848 sub addDevice {
849         my $handler = shift;
850         my $device = shift;
851         my $path = shift;
852         my $options = shift;
853         my $blocksize = shift;
854
855         $options =~ s/\,/ /;
856
857         my $_handler = $_HANDLER_MAP_{$handler};
858
859         if (defined($$DEVICES{$device})) {
860                 print "WARNING: Device '$device' already defined.\n";
861                 return $TRUE;
862         }
863
864         print "\t-> Opening virtual device '$device' at path '$path' using handler '$handler'..\n";
865
866         if ($SCST->openDevice($_handler, $device, $path, $options, $blocksize)) {
867                 print "WARNING: Failed to open virtual device '$device' at path '$path'.\n";
868                 return $TRUE;
869         }
870
871         $$DEVICES{$device} = $_handler;
872
873         return $FALSE;
874 }
875
876 sub removeDevice {
877         my $handler = shift;
878         my $device = shift;
879
880         my $_handler = $_HANDLER_MAP_{$handler};
881
882         if (!defined($$DEVICES{$device})) {
883                 print "WARNING: Device '$device' not defined.\n";
884                 return $TRUE;
885         }
886
887         print "\t-> Closing virtual device '$device'..\n";
888
889         if ($SCST->closeDevice($_handler, $device)) {
890                 print "WARNING: Failed to close virtual device '$device'.\n";
891                 return $TRUE;
892         }
893
894         undef $$DEVICES{$device};
895
896         return $FALSE;
897 }
898
899 sub addGroup {
900         my $group = shift;
901
902         if (defined($GROUPS{$group})) {
903                 print "WARNING: Group '$group' already exists.\n";
904                 return $TRUE;
905         }
906
907         print "\t-> Creating security group '$group'..\n";
908
909         if ($SCST->addGroup($group)) {
910                 print "WARNING: Failed to create security group '$group'.\n";
911                 return $TRUE;
912         }
913
914         $GROUPS{$group}++;
915
916         return $FALSE;
917 }
918
919 sub removeGroup {
920         my $group = shift;
921
922         if (!defined($GROUPS{$group})) {
923                 print "WARNING: Group '$group' does not exist.\n";
924                 return $TRUE;
925         }
926
927         print "\t-> Removing security group '$group'..\n";
928
929         if ($SCST->removeGroup($group)) {
930                 print "WARNING: Failed to remove security group '$group'.\n";
931                 return $TRUE;
932         }
933
934         undef $GROUPS{$group};
935
936         return $FALSE;
937 }
938
939 sub addUser {
940         my $group = shift;
941         my $user = shift;
942
943         if (!defined($GROUPS{$group})) {
944                 print "WARNING: Failed to add user '$user' to group '$group', group does not exist.\n";
945                 return $TRUE;
946         }
947
948         if (defined($USERS{$group}->{$user})) {
949                 print "WARNING: User '$user' already exists in security group '$group'.\n";
950                 return $TRUE;
951         }
952
953         print "\t-> Adding user '$user' to security group '$group'..\n";
954
955         if ($SCST->addUser($user, $group)) {
956                 print "WARNING: Failed to add user '$user' to security group '$group'.\n";
957                 return $TRUE;
958         }
959
960         $USERS{$group}->{$user}++;
961
962         return $FALSE;
963 }
964
965 sub removeUser {
966         my $group = shift;
967         my $user = shift;
968
969         if (!defined($GROUPS{$group})) {
970                 print "WARNING: Failed to remove user '$user' from group '$group', group does not exist.\n";
971                 return $TRUE;
972         }
973
974         if (!defined($USERS{$group}->{$user})) {
975                 print "WARNING: User '$user' doesn\'t exist in security group '$group'.\n";
976                 return $TRUE;
977         }
978
979         print "\t-> Removing user '$user' from security group '$group'..\n";
980
981         if ($SCST->removeUser($user, $group)) {
982                 print "WARNING: Failed to add user '$user' to security group '$group'.\n";
983                 return $TRUE;
984         }
985
986         undef $USERS{$group}->{$user};
987
988         return $FALSE;
989 }
990
991 sub clearUsers {
992         my $group = shift;
993
994         if (!defined($GROUPS{$group})) {
995                 print "WARNING: Failed to clear users from group '$group', group does not exist.\n";
996                 return $TRUE;
997         }
998
999         print "\t-> Clearing users from security group '$group'..\n";
1000
1001         if ($SCST->clearUsers($group)) {
1002                 print "WARNING: Failed to clear users from security group '$group'.\n";
1003                 return $TRUE;
1004         }
1005
1006         undef $USERS{$group};
1007
1008         return $FALSE;
1009 }
1010
1011 sub assignDevice {
1012         my $group = shift;
1013         my $device = shift;
1014         my $lun = shift;
1015         my %allLuns;
1016
1017         # Put luns into something easier to parse..
1018         foreach my $_group (keys %ASSIGNMENTS) {
1019                 my $_gAssigns = $ASSIGNMENTS{$_group};
1020
1021                 foreach my $_device (keys %{$_gAssigns}) {
1022                         @{$allLuns{$_group}}[$$_gAssigns{$_device}] = $_device;
1023                 }
1024         }
1025
1026         # Use the next available LUN if none specified
1027         if ($lun !~ /\d+/) {
1028                 $lun = ($#{$allLuns{$group}} + 1);
1029                 if ($lun > $_MAX_LUNS_) {
1030                         print "ERROR: Unable to use next available LUN of $lun, lun out of range.\n";
1031                         return $TRUE;
1032                 }
1033
1034                 print "\t-> Device '$device': Using next available LUN of $lun for group '$group'.\n";
1035         }
1036                         
1037         if (($lun < 0) || ($lun > $_MAX_LUNS_)) {
1038                 print "ERROR: Unable to assign device '$device', lun '$lun' is out of range.\n";
1039                 return $TRUE;
1040         }
1041
1042         if (!defined($$DEVICES{$device})) {
1043                 print "WARNING: Unable to assign non-existant device '$device' to group '$group'.\n";
1044                 return $TRUE;
1045         }
1046
1047         if (@{$allLuns{$group}}[$lun]) {
1048                 print "ERROR: Device '$device': Lun '$lun' is already assigned to device '".@{$allLuns{$group}}[$lun]."'.\n";
1049                 return $TRUE;
1050         }
1051
1052         print "\t-> Assign virtual device '$device' to group '$group' at LUN '$lun'..\n";
1053
1054         if ($SCST->assignDeviceToGroup($device, $group, $lun)) {
1055                 print "WARNING: Failed to assign device '$device' to group '$group'.\n";
1056                 return $TRUE;
1057         }
1058
1059         if (!defined($ASSIGNMENTS{$group})) {
1060                 my %assignments_t;
1061                 $ASSIGNMENTS{$group} = \%assignments_t;
1062         }
1063
1064         my $_assignments = $ASSIGNMENTS{$group};
1065
1066         $$_assignments{$device} = $lun; 
1067
1068         return $FALSE;
1069 }
1070
1071 sub releaseDevice {
1072         my $group = shift;
1073         my $device = shift;
1074
1075         if (!defined($GROUPS{$group})) {
1076                 print "WARNING: Failed to release device '$device' from group '$group', group does not exist.\n";
1077                 return $TRUE;
1078         }
1079
1080         if (!defined($$DEVICES{$device})) {
1081                 print "WARNING: Failed to release device '$device', device not defined.\n";
1082                 return $TRUE;
1083         }
1084
1085         print "\t-> Release virtual device '$device' from group '$group'..\n";
1086
1087         if ($SCST->removeDeviceFromGroup($device, $group)) {
1088                 print "WARNING: Failed to release device '$device' from group '$group'.\n";
1089                 return $TRUE;
1090         }
1091
1092         my $_assignments = $ASSIGNMENTS{$group};
1093
1094         undef $$_assignments{$device};
1095
1096         return $FALSE;
1097 }
1098
1099 sub clearDevices {
1100         my $group = shift;
1101
1102         if (!defined($GROUPS{$group})) {
1103                 print "WARNING: Failed to clear devices from group '$group', group does not exist.\n";
1104                 return $TRUE;
1105         }
1106
1107         print "\t-> Clear virtual devices from group '$group'..\n";
1108
1109         if ($SCST->clearGroupDevices($group)) {
1110                 print "WARNING: Failed to clear devices from group '$group'.\n";
1111                 return $TRUE;
1112         }
1113
1114         undef $ASSIGNMENTS{$group};
1115
1116         return $FALSE;
1117 }
1118
1119 sub targets {
1120         my %targets;
1121         my %fcards;
1122         my $root = new IO::Dir $_FC_CLASS_;
1123
1124         if ($root) {
1125                 while (my $entry = $root->read()) {
1126                         next if (($entry eq '.') || ($entry eq '..'));
1127
1128                         my $io = new IO::File "$_FC_CLASS_/$entry/port_name", O_RDONLY;
1129
1130                         if ($io) {
1131                                 my $wwn = <$io>;
1132                                 chomp $wwn;
1133                                 close $io;
1134
1135                                 $fcards{$entry} = $wwn;
1136                         }
1137                 }
1138         }
1139
1140         $root = new IO::Dir $_SCSI_CLASS_;
1141
1142         if ($root) {
1143                 while (my $entry = $root->read()) {
1144                         next if (($entry eq '.') || ($entry eq '..'));
1145
1146                         my $io = new IO::File "$_SCSI_CLASS_/$entry/target_mode_enabled", O_RDONLY;
1147
1148                         if ($io) {
1149                                 my $enabled = <$io>;
1150                                 chomp $enabled;
1151                                 close $io;
1152
1153                                 $targets{$entry}->{'path'} = "$_SCSI_CLASS_/$entry/target_mode_enabled";
1154                                 $targets{$entry}->{'enabled'} = $enabled;
1155
1156                                 if ($fcards{$entry}) {
1157                                         $targets{$fcards{$entry}}->{'enabled'} = $enabled;
1158                                         $targets{$fcards{$entry}}->{'path'} =
1159                                           "$_SCSI_CLASS_/$entry/target_mode_enabled";
1160                                         $targets{$entry}->{'duplicate'} = $TRUE;
1161                                 } else {
1162                                         $targets{$entry}->{'duplicate'} = $FALSE;
1163                                 }
1164                         }
1165                 }
1166         }
1167
1168         return \%targets;
1169 }
1170
1171 sub targetEnabled {
1172         my $target = shift;
1173
1174         return undef if (!defined($$TARGETS{$target}));
1175         return $$TARGETS{$target}->{'enabled'};
1176 }
1177
1178 sub enableTarget {
1179         my $target = shift;
1180         my $enable = shift;
1181
1182         return undef if (!defined($$TARGETS{$target}));
1183
1184         my $io = new IO::File $$TARGETS{$target}->{'path'}, O_WRONLY;
1185         return $TRUE if (!$io);
1186
1187         print $enable ? "\t-> Enabling" : "\t-> Disabling";
1188         print " target mode for SCST host '$target'.\n";
1189
1190         if ($_DEBUG_) {
1191                 print "DBG($$): ".$$TARGETS{$target}->{'path'}." -> $enable\n\n";
1192         } else {
1193                 print $io $enable;
1194         }
1195
1196         close $io;
1197
1198         $$TARGETS{unformatTarget($target)}->{'enabled'} = $enable;
1199
1200         return $FALSE;
1201 }
1202
1203 sub readConfig {
1204         my $confile = shift;
1205         my %config;
1206         my $section;
1207         my $arg;
1208
1209         my $io = new IO::File $confile, O_RDONLY;
1210
1211         die("FATAL: Unable to open specified configuration file $confile: $!\n") if (!$io);
1212
1213         while (my $line = <$io>) {
1214                 ($line, undef) = split(/\#/, $line, 2);
1215                 $line = cleanupString($line);
1216
1217                 if ($line =~ /^\[(.*)\]$/) {
1218                         ($section, $arg) = split(/\s+/, $1, 2);
1219                 } elsif ($section && $arg && $line) {
1220                         my($parameter, $value) = split(/\s+/, $line, 2);
1221                         push @{$config{$section}->{$arg}->{$parameter}}, $value;
1222                 }
1223         }
1224
1225         close $io;
1226
1227         return \%config;
1228 }
1229
1230 sub cleanupString {
1231         my $string = shift;
1232
1233         $string =~ s/^\s+//;
1234         $string =~ s/\s+$//;
1235
1236         return $string;
1237 }
1238
1239 sub formatTarget {
1240         my $target = shift;
1241
1242         if ($target =~ /^0x/) {
1243                 $target =~ s/^0x//;
1244                 my($o1, $o2, $o3, $o4, $o5, $o6, $o7, $o8) = unpack("A2A2A2A2A2A2A2A2", $target);
1245                 $target = "$o1:$o2:$o3:$o4:$o5:$o6:$o7:$o8";
1246         }
1247
1248         $target =~ tr/A-Z/a-z/;
1249
1250         return $target;
1251 }
1252
1253 sub unformatTarget {
1254         my $target = shift;
1255
1256         if ($target =~ /^.{2}\:.{2}\:.{2}\:.{2}\:.{2}\:.{2}\:.{2}\:.{2}/) {
1257                 $target =~ s/\://g;
1258                 $target = "0x$target";
1259         }
1260
1261         $target =~ tr/A-Z/a-z/;
1262
1263         return $target;
1264 }
1265
1266 # Hey! Stop that!
1267 sub commitSuicide {
1268         print "\n\nAborting immediately.\n";
1269         exit 1;
1270 }