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