- Fixed config file parsing for null groups when a group
[mirror/scst/.git] / scstadmin / SCST / SCST.pm
1 package SCST::SCST;
2
3 # Author:       Mark R. Buechler
4 # License:      GPLv2
5 # Copyright (c) 2005-2007 Mark R. Buechler
6
7 use 5.005;
8 use IO::Handle;
9 use IO::File;
10 use strict;
11 use Carp;
12
13 my $TRUE  = 1;
14 my $FALSE = 0;
15
16 my $_SCST_DIR_           = '/proc/scsi_tgt';
17 my $_SCST_IO_            = $_SCST_DIR_.'/scsi_tgt';
18 my $_SCST_CDROM_IO_      = $_SCST_DIR_.'/dev_cdrom/dev_cdrom';
19 my $_SCST_CHANGER_IO_    = $_SCST_DIR_.'/dev_changer/dev_changer';
20 my $_SCST_DISK_IO_       = $_SCST_DIR_.'/dev_disk/dev_disk';
21 my $_SCST_DISKP_IO_      = $_SCST_DIR_.'/dev_disk_perf/dev_disk_perf';
22 my $_SCST_MODISK_IO_     = $_SCST_DIR_.'/dev_modisk/dev_modisk';
23 my $_SCST_MODISKP_IO_    = $_SCST_DIR_.'/dev_modisk_perf/dev_modisk_perf';
24 my $_SCST_TAPE_IO_       = $_SCST_DIR_.'/dev_tape/dev_tape';
25 my $_SCST_TAPEP_IO_      = $_SCST_DIR_.'/dev_tape_perf/dev_tape_perf';
26 my $_SCST_VDISK_IO_      = $_SCST_DIR_.'/vdisk/vdisk';
27 my $_SCST_VCDROM_IO_     = $_SCST_DIR_.'/vcdrom/vcdrom';
28 my $_SCST_PROCESSOR_IO_  = $_SCST_DIR_.'/dev_processor/dev_processor';
29 my $_SCST_GROUPS_DIR_    = $_SCST_DIR_.'/groups';
30 my $_SCST_SGV_STATS_     = $_SCST_DIR_.'/sgv';
31 my $_SCST_SESSIONS_      = $_SCST_DIR_.'/sessions';
32 my $_SCST_VERSION_IO_    = $_SCST_DIR_.'/version';
33
34 my $_SCST_USERS_IO_      = 'names';
35 my $_SCST_DEVICES_IO_    = 'devices';
36
37 use vars qw(@ISA @EXPORT $VERSION $CDROM_TYPE $CHANGER_TYPE $DISK_TYPE $VDISK_TYPE
38             $VCDROM_TYPE $DISKPERF_TYPE $MODISK_TYPE $MODISKPERF_TYPE $TAPE_TYPE
39             $TAPEPERF_TYPE $PROCESSOR_TYPE $IOTYPE_PHYSICAL $IOTYPE_VIRTUAL
40             $IOTYPE_PERFORMANCE);
41
42 $CDROM_TYPE         = 1;
43 $CHANGER_TYPE       = 2;
44 $DISK_TYPE          = 3;
45 $VDISK_TYPE         = 4;
46 $VCDROM_TYPE        = 5;
47 $DISKPERF_TYPE      = 6;
48 $MODISK_TYPE        = 7;
49 $MODISKPERF_TYPE    = 8;
50 $TAPE_TYPE          = 9;
51 $TAPEPERF_TYPE      = 10;
52 $PROCESSOR_TYPE     = 11;
53
54 $IOTYPE_PHYSICAL    = 100;
55 $IOTYPE_VIRTUAL     = 101;
56 $IOTYPE_PERFORMANCE = 102;
57
58 $VERSION = 0.7.3;
59
60 my $_SCST_MIN_MAJOR_   = 0;
61 my $_SCST_MIN_MINOR_   = 9;
62 my $_SCST_MIN_RELEASE_ = 6;
63
64 my %_IO_MAP_ = ($CDROM_TYPE => $_SCST_CDROM_IO_,
65                 $CHANGER_TYPE => $_SCST_CHANGER_IO_,
66                 $DISK_TYPE => $_SCST_DISK_IO_,
67                 $VDISK_TYPE => $_SCST_VDISK_IO_,
68                 $VCDROM_TYPE => $_SCST_VCDROM_IO_,
69                 $DISKPERF_TYPE => $_SCST_DISKP_IO_,
70                 $MODISK_TYPE => $_SCST_MODISK_IO_,
71                 $MODISKPERF_TYPE => $_SCST_MODISKP_IO_,
72                 $TAPE_TYPE => $_SCST_TAPE_IO_,
73                 $TAPEPERF_TYPE => $_SCST_TAPEP_IO_,
74                 $PROCESSOR_TYPE => $_SCST_PROCESSOR_IO_);
75
76 my %_TYPE_MAP_ = ('dev_cdrom' => $CDROM_TYPE,
77                   'dev_changer' => $CHANGER_TYPE,
78                   'dev_disk' => $DISK_TYPE,
79                   'vdisk' => $VDISK_TYPE,
80                   'vcdrom' => $VCDROM_TYPE,
81                   'dev_disk_perf' => $DISKPERF_TYPE,
82                   'dev_modisk' => $MODISK_TYPE,
83                   'dev_modisk_perf' => $MODISKPERF_TYPE,
84                   'dev_tape' => $TAPE_TYPE,
85                   'dev_tape_perf' => $TAPEPERF_TYPE,
86                   'dev_processor' => $PROCESSOR_TYPE);
87
88 my %_REVERSE_MAP_ = ($CDROM_TYPE => 'dev_cdrom',
89                      $CHANGER_TYPE => 'dev_changer',
90                      $DISK_TYPE => 'dev_disk',
91                      $VDISK_TYPE => 'vdisk',
92                      $VCDROM_TYPE => 'vcdrom',
93                      $DISKPERF_TYPE => 'dev_disk_perf',
94                      $MODISK_TYPE => 'dev_modisk',
95                      $MODISKPERF_TYPE => 'dev_modisk_perf',
96                      $TAPE_TYPE => 'dev_tape',
97                      $TAPEPERF_TYPE => 'dev_tape_perf',
98                      $PROCESSOR_TYPE => 'dev_processor');
99
100 my %_IO_TYPES_ = ($CDROM_TYPE => $IOTYPE_PHYSICAL,
101                   $CHANGER_TYPE => $IOTYPE_PHYSICAL,
102                   $DISK_TYPE => $IOTYPE_PHYSICAL,
103                   $VDISK_TYPE => $IOTYPE_VIRTUAL,
104                   $VCDROM_TYPE => $IOTYPE_VIRTUAL,
105                   $DISKPERF_TYPE => $IOTYPE_PERFORMANCE,
106                   $MODISK_TYPE => $IOTYPE_PHYSICAL,
107                   $MODISKPERF_TYPE => $IOTYPE_PERFORMANCE,
108                   $TAPE_TYPE => $IOTYPE_PHYSICAL,
109                   $TAPEPERF_TYPE => $IOTYPE_PERFORMANCE,
110                   $PROCESSOR_TYPE => $IOTYPE_PHYSICAL);
111
112 my %_HANDLER_ALIASES_ = ('vdisk_blk' => 'vdisk');
113
114 my %_AVAILABLE_OPTIONS_ = ('WRITE_THROUGH' => 'WRITE_THROUGH',
115                            'O_DIRECT'      => 'O_DIRECT',
116                            'READ_ONLY'     => 'READ_ONLY',
117                            'NULLIO'        => 'NULLIO',
118                            'NV_CACHE'      => 'NV_CACHE',
119                            'BLOCKIO'       => 'BLOCKIO',
120                            'BIO'           => 'BLOCKIO');
121
122 sub new {
123         my $this = shift;
124         my $debug = shift;
125         my $badVersion = $TRUE;
126         
127         my $class = ref($this) || $this;
128         my $self = {};
129
130         bless($self, $class);
131
132         $self->{'debug'} = $debug;
133
134         my $scstVersion = $self->scstVersion();
135
136         my($major, $minor, $release) = split(/\./, $scstVersion, 3);
137         ($release, undef) = split(/\-/, $release) if ($release =~ /\-/);
138
139         $badVersion = $FALSE if (($major > $_SCST_MIN_MAJOR_) ||
140                                  (($major == $_SCST_MIN_MAJOR_) && ($minor > $_SCST_MIN_MINOR_)) ||
141                                  (($major == $_SCST_MIN_MAJOR_) && ($minor == $_SCST_MIN_MINOR_) && ($release >= $_SCST_MIN_RELEASE_)));
142
143         croak("This module requires at least SCST version $_SCST_MIN_MAJOR_\.$_SCST_MIN_MINOR_\.".
144               "$_SCST_MIN_RELEASE_ and version $scstVersion was found") if ($badVersion);
145
146         return $self;
147 }
148
149 sub scstVersion {
150         my $self = shift;
151
152         my $io = new IO::File $_SCST_VERSION_IO_, O_RDONLY;
153         return $TRUE if (!$io);
154
155         my $version = <$io>;
156         chomp $version;
157
158         return $version;
159 }
160
161 sub groups {
162         my $self = shift;
163         my @groups;
164         my $dirHandle = new IO::Handle;
165
166         opendir $dirHandle, $_SCST_GROUPS_DIR_ or return undef;
167       
168         foreach my $entry (readdir($dirHandle)) {
169                 next if (($entry eq '.') || ($entry eq '..'));
170
171                 push @groups, $entry;
172         }
173
174         close $dirHandle;
175
176         return \@groups;
177 }
178
179 sub groupExists {
180         my $self = shift;
181         my $group = shift;
182         my $groups = $self->groups();
183
184         foreach my $_group (@{$groups}) {
185                 return $TRUE if ($group eq $_group);
186         }
187
188         return $FALSE;
189 }
190
191 sub addGroup {
192         my $self = shift;
193         my $group = shift;
194
195         return 2 if ($self->groupExists($group));
196
197         my $io = new IO::File $_SCST_IO_, O_WRONLY;
198
199         if (!$io) {
200                 $self->{'error'} = "addGroup(): Failed to open handler IO $_SCST_IO_";
201                 return $TRUE;
202         }
203
204         my $cmd = "add_group $group\n";
205
206         if ($self->{'debug'}) {
207                 print "DBG($$): $_SCST_IO_ -> $cmd\n";
208         } else {
209                 print $io $cmd;
210         }
211
212         close $io;
213
214         return $FALSE if ($self->{'debug'});
215         return !$self->groupExists($group);
216 }
217
218 sub removeGroup {
219         my $self = shift;
220         my $group = shift;
221
222         return 2 if (!$self->groupExists($group));
223
224         my $io = new IO::File $_SCST_IO_, O_WRONLY;
225
226         if (!$io) {
227                 $self->{'error'} = "removeGroup(): Failed to open handler IO $_SCST_IO_";
228                 return $TRUE;
229         }
230
231         my $cmd = "del_group $group\n";
232
233         if ($self->{'debug'}) {
234                 print "DBG($$): $_SCST_IO_ -> $cmd\n";
235         } else {
236                 print $io $cmd;
237         }
238
239         close $io;
240
241         return $FALSE if ($self->{'debug'});
242         return $self->groupExists($group);
243 }
244
245 sub sgvStats {
246         my $self = shift;
247         my $io = new IO::File $_SCST_SGV_STATS_, O_RDONLY;
248         my %stats;
249         my $first = $TRUE;
250
251         if (!$io) {
252                 $self->{'error'} = "sgvStats(): Failed to open handler IO $_SCST_IO_";
253                 return undef;
254         }
255
256         while (my $line = <$io>) {
257                 chomp $line;
258
259                 if ($first || !$line) {
260                         $first = $FALSE;
261                         next;
262                 }
263
264                 my $size;
265                 my $stat;
266                 my $hit;
267                 my $total;
268
269                 if ($line !~ /^\s/) {
270                         ($stat, $hit, $total) = split(/\s+/, $line);
271
272                         $size = 'ALL';
273                         if ($stat eq 'big') {
274                                 $total = $hit;
275                                 $hit = -1;
276                         }
277                 } else {
278                         (undef, $stat, $hit, $total) = split(/\s+/, $line);
279
280                         if ($stat =~ /(\d+)K$/) {
281                                 $size = $1;
282                                 $stat =~ s/\-$size\K//;
283                         }
284                 }
285
286                 $stats{$stat}->{$size}->{'HITS'} = $hit;
287                 $stats{$stat}->{$size}->{'TOTAL'} = $total;
288         }
289
290         close $io;
291
292         return \%stats;
293 }
294
295 sub sessions {
296         my $self = shift;
297         my $io = new IO::File $_SCST_SESSIONS_, O_RDONLY;
298         my %sessions;
299         my $first = $TRUE;
300
301         if (!$io) {
302                 $self->{'error'} = "sessions(): Failed to open handler IO $_SCST_IO_";
303                 return undef;
304         }
305
306         while (my $line = <$io>) {
307                 chomp $line;
308
309                 if ($first) {
310                         $first = $FALSE;
311                         next;
312                 }
313
314                 my($target, $user, $group, $commands) = split(/\s+/, $line);
315
316                 $sessions{$target}->{$group}->{$user} = $commands;
317         }
318
319         close $io;
320
321         return \%sessions;
322 }
323
324 sub devices {
325         my $self = shift;
326         my $io = new IO::File $_SCST_IO_, O_RDONLY;
327         my %devices;
328         my $first = $TRUE;
329
330         if (!$io) {
331                 $self->{'error'} = "devices(): Failed to open handler IO $_SCST_IO_";
332                 return undef;
333         }
334
335         while (my $line = <$io>) {
336                 chomp $line;
337
338                 if ($first) {
339                         $first = $FALSE;
340                         next;
341                 }
342
343                 my($vname, $handler) = split(/\s+/, $line);
344
345                 $handler = $_HANDLER_ALIASES_{$handler} if ($_HANDLER_ALIASES_{$handler});
346                 $devices{$vname} = $_TYPE_MAP_{$handler};
347         }
348
349         close $io;
350
351         return \%devices;
352 }
353
354 sub handlerDevices {
355         my $self = shift;
356         my $handler = shift;
357         my $handler_io = $_IO_MAP_{$handler};
358         my $first = $TRUE;
359         my %devices;
360
361         if (!$handler_io) {
362                 $self->{'error'} = "handlerDevices(): Failed to open handler IO $handler_io or handler $handler invalid";
363                 return undef;
364         }
365
366         if (!$self->handlerExists($handler)) {
367                 $self->{'error'} = "handlerDevices(): Handler $handler does not exist";
368                 return undef;
369         }
370
371         my $io = new IO::File $handler_io, O_RDONLY;
372
373         if (!$io) {
374                 print "WARNING: handlerDevices(): Failed to open handler IO $handler_io, assuming disabled.\n";
375                 return \%devices; # Return an empty hash
376         }
377
378         while (my $line = <$io>) {
379                 chomp $line;
380
381                 if ($first) {
382                         $first = $FALSE;
383                         next;
384                 }
385
386                 my ($vname, $size, $blocksize, $options, $path) = split(/\s+/, $line);
387
388                 if ($options =~ /^\//) {
389                         $path = $options;
390                         $options = "";
391                 }
392
393                 $devices{$vname}->{'OPTIONS'} = cleanupString($options);
394                 $devices{$vname}->{'SIZE'} = cleanupString($size);
395                 $devices{$vname}->{'PATH'} = cleanupString($path);
396                 $devices{$vname}->{'BLOCKSIZE'} = cleanupString($blocksize);
397         }
398
399         close $io;
400
401         return \%devices;
402 }
403
404 sub handlerDeviceExists {
405         my $self = shift;
406         my $handler = shift;
407         my $device = shift;
408         my $devices = $self->handlerDevices($handler);
409
410         return -1 if (!defined($devices));
411         return $TRUE if (defined($$devices{$device}));
412
413         return $FALSE;
414 }
415
416 sub handlerType {
417         my $self = shift;
418         my $handler = shift;
419
420         my $type = $_IO_TYPES_{$handler};
421
422         if (!$type) {
423                 $self->{'error'} = "handlerType(): Handler type for handler $handler not defined";
424                 return undef;
425         }
426
427         return $type;
428 }
429
430 sub openDevice {
431         my $self = shift;
432         my $handler = shift;
433         my $device = shift;
434         my $path = shift;
435         my $options = shift;
436         my $blocksize = shift;
437         my $handler_io = $_IO_MAP_{$handler};
438         my $valid_opts;
439
440         ($options, $valid_opts) = $self->checkOptions($options);
441
442         if (!$valid_opts) {
443                 $self->{'error'} = "openDevice(): Invalid option(s) '$options' given for device $device";
444                 return $TRUE;
445         }
446
447         if (!$handler_io) {
448                 $self->{'error'} = "openDevice(): Failed to open handler IO $handler_io or ".
449                   "handler $handler invalid";
450                 return $TRUE;
451         }
452
453         if (!$self->handlerExists($handler)) {
454                 $self->{'error'} = "openDevice(): Handler $handler does not exist";
455                 return $TRUE;
456         }
457
458         if ($self->handlerDeviceExists($handler, $device)) {
459                 $self->{'error'} = "openDevice(): Device $device is already open";
460                 return 2;
461         }
462
463         $options = cleanupString($options);
464
465         my $cmd = "open $device $path $blocksize $options\n";
466
467         $cmd = cleanupString($cmd);
468
469         my $rc = $self->handler_private($handler_io, $cmd);
470
471         return $FALSE if ($self->{'debug'});
472         return $rc if ($rc);
473
474         $rc = !$self->handlerDeviceExists($handler, $device);
475
476         if ($rc) {
477                 $self->{'error'} = "openDevice(): An error occured while opening device $device. ".
478                   "See dmesg/kernel log for more information.";
479         }
480
481         return $rc;
482 }
483
484 sub closeDevice {
485         my $self = shift;
486         my $handler = shift;
487         my $device = shift;
488         my $path = shift;
489         my $handler_io = $_IO_MAP_{$handler};
490
491         if (!$handler_io) {
492                 $self->{'error'} = "closeDevice(): Failed to open handler IO $handler_io or handler $handler invalid";
493                 return $TRUE;
494         }
495
496         if (!$self->handlerExists($handler)) {
497                 $self->{'error'} = "closeDevice(): Handler $handler does not exist";
498                 return $TRUE;
499         }
500
501         if (!$self->handlerDeviceExists($handler, $device)) {
502                 $self->{'error'} = "closeDevice(): Device $device is not open";
503                 return 2;
504         }
505
506         my $cmd = "close $device $path\n";
507
508         my $rc = $self->handler_private($handler_io, $cmd);
509
510         return $FALSE if ($self->{'debug'});
511         return $rc if ($rc);
512
513         $rc = $self->handlerDeviceExists($handler, $device);
514
515         if ($rc) {
516                 $self->{'error'} = "closeDevice(): An error occured while closing device $device. ".
517                   "See dmesg/kernel log for more information.";
518         }
519
520         return $rc;
521 }
522
523 sub userExists {
524         my $self = shift;
525         my $user = shift;
526         my $group = shift;
527
528         my $users = $self->users($group);
529
530         return -1 if (!defined($users));
531
532         foreach my $_user (@{$users}) {
533                 return $TRUE if ($user eq $_user);
534         }
535
536         return $FALSE;
537 }
538
539 sub users {
540         my $self = shift;
541         my $group = shift;
542         my @users;
543
544         return undef if (!$self->groupExists($group));
545
546         my $io = new IO::File $_SCST_GROUPS_DIR_."/$group/".$_SCST_USERS_IO_, O_RDONLY;
547
548         if (!$io) {
549                 $self->{'error'} = "users(): Failed to open handler IO ".$_SCST_GROUPS_DIR_.
550                   "/$group/".$_SCST_USERS_IO_;
551                 return undef;
552         }
553
554         while (my $line = <$io>) {
555                 chomp $line;
556                 
557                 push @users, $line;
558         }
559
560         close $io;
561
562         return \@users;
563 }
564
565 sub addUser {
566         my $self = shift;
567         my $user = shift;
568         my $group = shift;
569
570         if (!$self->groupExists($group)) {
571                 $self->{'error'} = "addUser(): Group $group does not exist";
572                 return $TRUE;
573         }
574
575         if ($self->userExists($user, $group)) {
576                 $self->{'error'} = "addUser(): User $user already exists in group $group";
577                 return 2;
578         }
579
580         my $cmd = "add $user\n";
581
582         my $rc = $self->group_private($group, $_SCST_USERS_IO_, $cmd);
583
584         return $FALSE if ($self->{'debug'});
585         return $rc if ($rc);
586
587         $rc = !$self->userExists($user, $group);
588
589         if ($rc) {
590                 $self->{'error'} = "addUser(): An error occured while adding user $user to group $group. ".
591                   "See dmesg/kernel log for more information.";
592         }
593
594         return $rc;
595 }
596
597 sub removeUser {
598         my $self = shift;
599         my $user = shift;
600         my $group = shift;
601
602         if (!$self->groupExists($group)) {
603                 $self->{'error'} = "removeUser(): Group $group does not exist";
604                 return $TRUE;
605         }
606
607         if ($self->userExists($user, $group)) {
608                 $self->{'error'} = "removeUser(): User $user does not exist in group $group";
609                 return 2;
610         }
611
612         my $cmd = "del $user\n";
613
614         my $rc = $self->group_private($group, $_SCST_USERS_IO_, $cmd);
615
616         return $FALSE if ($self->{'debug'});
617         return $rc if ($rc);
618
619         $rc = $self->userExists($user, $group);
620
621         if ($rc) {
622                 $self->{'error'} = "removeUser(): An error occured while removing user $user ".
623                   "from group $group. See dmesg/kernel log for more information.";
624         }
625
626         return $rc;
627 }
628
629 sub clearUsers {
630         my $self = shift;
631         my $group = shift;
632
633         if (!$self->groupExists($group)) {
634                 $self->{'error'} = "clearUsers(): Group $group does not exist";
635                 return $TRUE;
636         }
637
638         my $cmd = "clear\n";
639
640         my $rc = $self->group_private($group, $_SCST_USERS_IO_, $cmd);
641
642         return $FALSE if ($self->{'debug'});
643
644         if ($rc) {
645                 $self->{'error'} = "clearUsers(): An error occured while clearing users from ".
646                   "group $group. See dmesg/kernel log for more information.";
647                 return $rc;
648         }
649
650         my $users = $self->users($group);
651
652         return ($#{$users} + 1);
653 }
654
655 sub handlerExists {
656         my $self = shift;
657         my $handler = shift;
658
659         my $handlers = $self->handlers();
660
661         foreach my $_handler (@{$handlers}) {
662                 return $TRUE if ($handler eq $_handler);
663         }
664
665         return $FALSE;
666 }
667
668 sub handlers {
669         my $self = shift;
670         my @handlers;
671
672         my $dirHandle = new IO::Handle;
673
674         opendir $dirHandle, $_SCST_DIR_ or return undef;
675
676         foreach my $entry (readdir($dirHandle)) {
677                 next if (($entry eq '.') || ($entry eq '..'));
678                 if ((-d $_SCST_DIR_.'/'.$entry ) && (-f $_SCST_DIR_.'/'.$entry.'/type')) {
679                         push @handlers, $_TYPE_MAP_{$entry} if ($_TYPE_MAP_{$entry});
680                 }
681         }
682
683         close $dirHandle;
684
685         return \@handlers;
686 }
687
688 sub groupDeviceExists {
689         my $self = shift;
690         my $device = shift;
691         my $group = shift;
692         my $lun = shift;
693         my $devices = $self->groupDevices($group);
694
695         return -1 if (!defined($devices));
696
697         if (defined($lun)) {
698                 return $TRUE if ($$devices{$device} eq $lun);
699         } else {
700                 return $TRUE if (defined($$devices{$device}));
701         }
702
703         return $FALSE;
704 }
705
706 sub groupDevices {
707         my $self = shift;
708         my $group = shift;
709         my %devices;
710         my $first = $TRUE;
711
712         if (!$self->groupExists($group)) {
713                 $self->{'error'} = "groupDevices(): Group $group does not exist";
714                 return undef;
715         }
716
717         my $io = new IO::File $_SCST_GROUPS_DIR_."/$group/".$_SCST_DEVICES_IO_, O_RDONLY;
718
719         if (!$io) {
720                 $self->{'error'} = "groupDevices(): Failed to open handler IO ".$_SCST_GROUPS_DIR_.
721                   "/$group/".$_SCST_DEVICES_IO_;
722                 return undef;
723         }
724
725         while (my $line = <$io>) {
726                 chomp $line;
727
728                 if ($first) {
729                         $first = $FALSE;
730                         next;
731                 }
732
733                 my($vname, $lun) = split(/\s+/, $line);
734                 
735                 $devices{$vname} = $lun;
736         }
737
738         close $io;
739
740         return \%devices;
741 }
742
743 sub assignDeviceToGroup {
744         my $self = shift;
745         my $device = shift;
746         my $group = shift;
747         my $lun = shift;
748
749         if (!$self->groupExists($group)) {
750                 $self->{'error'} = "assignDeviceToGroup(): Group $group does not exist";
751                 return $TRUE;
752         }
753
754         if ($self->groupDeviceExists($device, $group, $lun)) {
755                 $self->{'error'} = "assignDeviceToGroup(): Device $device is already ".
756                   "assigned to group $group";
757                 return 2;
758         }
759
760         my $cmd = "add $device $lun\n";
761
762         my $rc = $self->group_private($group, $_SCST_DEVICES_IO_, $cmd);
763
764         return $FALSE if ($self->{'debug'});
765         return $rc if ($rc);
766
767         $rc = !$self->groupDeviceExists($device, $group, $lun);
768
769         if ($rc) {
770                 $self->{'error'} = "assignDeviceToGroup(): An error occured while assigning device $device ".
771                   "to group $group. See dmesg/kernel log for more information.";
772         }
773
774         return $rc;
775 }
776
777 sub assignDeviceToHandler {
778         my $self = shift;
779         my $device = shift;
780         my $handler = shift;
781         my $handler_io = $_IO_MAP_{$handler};
782         my $_handler = $_REVERSE_MAP_{$handler};
783
784         if (!$handler_io) {
785                 $self->{'error'} = "assignDeviceToHandler(): Failed to open handler IO $handler_io or ".
786                   "handler $_handler($handler) invalid";
787                 return $TRUE;
788         }
789
790         if (!$self->handlerExists($handler)) {
791                 $self->{'error'} = "assignDeviceToHandler(): Handler $_handler does not exist";
792                 return $TRUE;
793         }
794
795         if ($self->handlerDeviceExists($handler, $device)) {
796                 $self->{'error'} = "assignDeviceToHandler(): Device $device is already assigned to handler $_handler";
797                 return 2;
798         }
799
800         my $cmd = "assign $device $_handler\n";
801
802         my $rc = $self->scst_private($cmd);
803
804         return $FALSE if ($self->{'debug'});
805         return $rc if($rc);
806
807         $rc = !$self->handlerDeviceExists($handler, $device);
808
809         if ($rc) {
810                 $self->{'error'} = "assignDeviceToHandler(): An error occured while assigning device $device ".
811                   "to handler $_handler. See dmesg/kernel log for more information.";
812         }
813
814         return $rc;
815 }
816
817 sub removeDeviceFromGroup {
818         my $self = shift;
819         my $device = shift;
820         my $group = shift;
821
822         if (!$self->groupExists($group)) {
823                 $self->{'error'} = "removeDeviceFromGroup(): Group $group does not exist";
824                 return $TRUE;
825         }
826
827         if (!$self->groupDeviceExists($device, $group)) {
828                 $self->{'error'} = "removeDeviceFromGroup(): Device $device does not exist in group $group";
829                 return 2;
830         }
831
832         my $cmd = "del $device\n";
833
834         my $rc = $self->group_private($group, $_SCST_DEVICES_IO_, $cmd);
835
836         return $FALSE if ($self->{'debug'});
837         return $rc if ($rc);
838
839         $rc = $self->groupDeviceExists($device, $group);
840
841         if ($rc) {
842                 $self->{'error'} = "removeDeviceFromGroup(): An error occured while removing device $device ".
843                   "from group $group. See dmesg/kernel log for more information.";
844         }
845
846         return $rc;
847 }
848
849 sub clearGroupDevices {
850         my $self = shift;
851         my $group = shift;
852
853         return $TRUE if (!$self->groupExists($group));
854
855         my $cmd = "clear\n";
856
857         my $rc = $self->group_private($group, $_SCST_DEVICES_IO_, $cmd);
858
859         return $FALSE if ($self->{'debug'});
860
861         if ($rc) {
862                 $self->{'error'} = "clearGroupDevices(): An error occured while clearing devices from ".
863                   "group $group. See dmesg/kernel log for more information.";
864                 return $rc;
865         }
866
867         my $devices = $self->groupDevices($group);
868
869         return (keys %{$devices});
870 }
871
872 sub handler_private {
873         my $self = shift;
874         my $handler_io = shift;
875         my $cmd = shift;
876
877         my $io = new IO::File $handler_io, O_WRONLY;
878
879         if (!$io) {
880                 print "WARNING: SCST/SCST.pm: Failed to open handler IO $handler_io, assuming disabled.\n";
881                 return $FALSE;
882         }
883
884         if ($self->{'debug'}) {
885                 print "DBG($$): '$handler_io' -> '$cmd'\n";
886         } else {
887                 print $io "$cmd\0";
888         }
889
890         close $io;
891
892         return $FALSE;
893 }
894
895 sub scst_private {
896         my $self = shift;
897         my $cmd = shift;
898
899         my $io = new IO::File $_SCST_IO_, O_WRONLY;
900
901         if (!$io) {
902                 $self->{'error'} = "SCST/SCST.pm: Failed to open handler IO $_SCST_IO_";
903                 return $TRUE;
904         }
905
906         if ($self->{'debug'}) {
907                 print "DBG($$): '$_SCST_IO_' -> '$cmd'\n";
908         } else {
909                 print $io "$cmd\0";
910         }
911
912         close $io;
913
914         return $FALSE;
915 }
916
917 sub group_private {
918         my $self = shift;
919         my $group = shift;
920         my $file = shift;
921         my $cmd = shift;
922
923         my $io = new IO::File $_SCST_GROUPS_DIR_."/$group/".$file, O_WRONLY;
924
925         if (!$io) {
926                 $self->{'error'} = "SCST/SCST.pm: Failed to open handler IO ".$_SCST_GROUPS_DIR_."/$group/".$file;
927                 return $TRUE;
928         }
929
930         if ($self->{'debug'}) {
931                 print "DBG($$): $_SCST_GROUPS_DIR_/$group/$file -> $cmd\n";
932         } else {
933                 print $io "$cmd\0";
934         }
935
936         close $io;
937
938         return $FALSE;
939 }
940
941 sub checkOptions {
942         my $self = shift;
943         my $options = shift;
944         my $o_string;
945
946         return undef, $TRUE if (!$options);
947
948         foreach my $option (split(/\s+/, $options)) {
949                 my $map = $_AVAILABLE_OPTIONS_{$option};
950                 return undef, $FALSE if (!$map);
951                 $o_string .= ",$map";
952         }
953
954         $o_string =~ s/^\,//;
955
956         return $o_string, $TRUE;
957 }
958
959 sub errorString {
960         my $self = shift;
961
962         return undef if (!$self->{'error'});
963
964         my $string = $self->{'error'};
965         $self->{'error'} = undef;
966
967         return $string;
968 }
969
970 sub cleanupString {
971         my $string = shift;
972
973         $string =~ s/^\s+//;
974         $string =~ s/\s+$//;
975
976         return $string;
977 }
978
979 ;1 __END__
980
981 =head1 NAME
982
983 SCST::SCST - Generic SCST methods.
984
985 =head1 SYNOPSIS
986
987     use SCST::SCST;
988
989     $p = SCST::SCST->new();
990     
991     print "Using SCST version".$p->scstVersion()."\n";
992     
993     if ($p->handlerDeviceExists($SCST::SCST::VDISK_TYPE)) {
994          print "openDevice() failed\n"
995            if ($p->openDevice($SCST::SCST::VDISK_TYPE, 'DISK01', '/vdisk/disk01.dsk'));
996     }
997     
998     undef $p;
999
1000 =head1 DESCRIPTION
1001
1002 Generic SCST methods.
1003
1004 =head2 Methods
1005
1006 =over 5
1007
1008 =item SCST::SCST->new();
1009
1010 Create a new SCST object. If the argument $debug is non-zero no changes
1011 will be made.
1012
1013 Arguments: (bool) $debug
1014
1015 Returns: (object) $new
1016
1017 =item SCST::SCST->scstVersion();
1018
1019 Returns the version of SCST running.
1020
1021 Arguments: void
1022
1023 Returns: (string) $version
1024
1025 =item SCST::SCST->groups();
1026
1027 Returns a list of security groups configured.
1028
1029 Arguments: void
1030
1031 Returns: (array ref) $groups
1032
1033 =item SCST::SCST->groupExists();
1034
1035 Checks for a specified group's existance.
1036
1037 Arguments: (string) $group
1038
1039 Returns: (boolean) $groupExists
1040
1041 =item SCST::SCST->addGroup();
1042
1043 Adds a security group to SCST's configuration. Returns 0 upon success, 1 if
1044 unsuccessfull and  2 if the group already exists.
1045
1046 Arguments: (string) $group
1047
1048 Returns: (int) $success
1049
1050 =item SCST::SCST->removeGroup();
1051
1052 Removes a group from SCST's configuration. Returns 0 upon success, 1 if
1053 unsuccessfull and 2 if group does not exist.
1054
1055 =item SCST::SCST->sgvStats();
1056
1057 Returns a hash of stats gathered from /proc/scsi_tgt/sgv.
1058
1059 Arguments: void
1060
1061 Returns: (hash ref) $stats
1062
1063 Hash Layout: See /proc/scsi_tgt/sgv for tokens. This methods simply hashes
1064 what's found there and returns it with no further processing.
1065
1066 =item SCST::SCST->sessions();
1067
1068 Returns a hash of current SCST initiator sessions. 
1069
1070 Arguments: void
1071
1072 Returns: (hash ref) $sessions
1073
1074 Hash Layout: See /proc/scsi_tgt/sessions for tokens. This methods simply hashes
1075 what's found there and returns it with no further processing.
1076
1077 =item SCST::SCST->devices();
1078
1079 Returns a hash of devices configured without regard to device handler.
1080
1081 Arguments: void
1082
1083 Returns: (hash ref) $devices
1084
1085 Hash Layout: (string) $device = (int) $handler
1086
1087 =item SCST::SCST->handlerDevices();
1088
1089 Returns a hash of devices configured for a specified device handler.
1090
1091 Arguments: (int) $handler
1092
1093 Returns: (hash ref) $devices
1094
1095 Hash Layout: (string) $device -> SIZE = (int) $deviceSize
1096              (string) $device -> PATH = (string) $devicePath
1097              (string) $device -> OPTIONS = (string) $options (comma seperated)
1098
1099 =item SCST::SCST->handlerDeviceExists();
1100
1101 Checks for a specified device is configured for a specified device handler.
1102
1103 Arguments: (int) $handler, (string) $device
1104
1105 Returns: (boolean) $deviceExists
1106
1107 =item SCST::SCST->handlerType();
1108
1109 Return the handler type for the specified handler. Handler types are:
1110
1111   SCST::SCST::IOTYPE_PHYSICAL
1112   SCST::SCST::IOTYPE_VIRTUAL
1113   SCST::SCST::IOTYPE_PERFORMANCE
1114
1115 Arguments: (int) $handler
1116
1117 Returns: (int) $handler_type
1118
1119 =item SCST::SCST->openDevice();
1120
1121 Opens an already existing specified device for the specified device handler.
1122 Returns 0 upon success, 1 if unsuccessfull and 2 if the device already exists.
1123
1124 Available options for the parameter $options are: WRITE_THROUGH, READ_ONLY, O_DIRECT
1125
1126 Arguments: (int) $handler, (string) $device, (string) $path [, (string) $options]
1127
1128 Returns: (int) $success
1129
1130 =item SCST::SCST->closeDevice();
1131
1132 Closes an open device configured for the specified device handler. Returns
1133 0 upon success, 1 if unsuccessfull and 2 of the device does not exist.
1134
1135 Arguments: (int) $handler, (string) $device, (string) $path
1136
1137 Returns: (int) $success
1138
1139 =item SCST::SCST->userExists();
1140
1141 Checks for a specified user with the specified security group.
1142
1143 Arguments: (string) $user, (string) $group
1144
1145 Returns (boolean) $userExists
1146
1147 =item SCST::SCST->users();
1148
1149 Returns a list of users configured for a given security group.
1150
1151 Arguments: (string) $group
1152
1153 Returns: (hash ref) $users
1154
1155 =item SCST::SCST->addUser();
1156
1157 Adds the specified user to the specified security group. Returns 0
1158 upon success, 1 if unsuccessfull and 2 if the user already exists.
1159
1160 Arguments: (string) $user, (string) $group
1161
1162 Returns: (int) $success
1163
1164 =item SCST::SCST->removeUser();
1165
1166 Removed the specified user from the specified security group. Returns
1167 0 upon success, 1 if unsuccessfull and 2 if the user does not exist.
1168
1169 Arguments: (string) $user, (string) $group
1170
1171 Returns: (int) $success
1172
1173 =item SCST::SCST->clearUsers();
1174
1175 Removes all users from the specified security group. Returns 0 upon
1176 success or 1 if unsuccessfull.
1177
1178 Arguments: (string) $group
1179
1180 Returns: (int) $success
1181
1182 =item SCST::SCST->handlerExists();
1183
1184 Checks if a specified device handler exists within SCST's configuration.
1185
1186 Arguments: (int) $handler
1187
1188 Returns: (boolean) $handlerExists
1189
1190 =item SCST::SCST->handlers();
1191
1192 Returns a list of configured device handlers.
1193
1194 Arguments: void
1195
1196 Returns: (array ref) $handlers
1197
1198 =item SCST::SCST->groupDeviceExists();
1199
1200 Checks if a specified device is assigned to a specified security group.
1201 If the optional $lun argument is specified, this method also matches
1202 the lun.
1203
1204 Arguments: (string) $device, (string) $group [, (int) $lun]
1205
1206 Returns: (boolean) $deviceExists
1207
1208 =item SCST::SCST->groupDevices();
1209
1210 Returns a hash if devices assigned to the specified security group.
1211
1212 Arguments: (string) $group
1213
1214 Returns: (hash ref) $devices
1215
1216 Hash Layout: (string) $device = (int) $lun
1217
1218 =item SCST::SCST->assignDeviceToGroup();
1219
1220 Assigns the specified device to the specified security group. Returns
1221 0 upon success, 1 if unsuccessfull and 2 if the device has already
1222 been assigned to the specified security group.
1223
1224 Arguments: (string) $device, (string) $group, (int) $lun
1225
1226 Returns: (int) $success
1227
1228 =item SCST::SCST->assignDeviceToHandler();
1229
1230 Assigns specified device to specified handler. Returns 0 upon success,
1231 1 if unsuccessfull and 2 if the specified device is already assigned to
1232 the specified handler.
1233
1234 Arguments: (string) $device, (string) $handler
1235
1236 Returns: (int) $success
1237
1238 =item SCST::SCST->removeDeviceFromGroup();
1239
1240 Removes the specified device from the specified security group. Returns
1241 0 upon success, 1 if unsuccessfull and 2 if the device has not been
1242 assigned to the specified security group.
1243
1244 Arguments: (string) $device, (string) $group
1245
1246 Returns: (int) $success
1247
1248 =item SCST::SCST->clearGroupDevices();
1249
1250 Removes all devices from the specified security group. Returns 0 upon
1251 success or 1 if unsuccessfull.
1252
1253 Arguments: (string) $group
1254
1255 Returns: (int) $success
1256
1257 =item SCST::SCST->errorString();
1258
1259 Contains a description of the last error occured or undef if no error
1260 has occured or if this method has already been called once since the
1261 last error.
1262
1263 Arguments: (void)
1264
1265 Returns: (string) $error_string
1266
1267 =back
1268
1269 =head1 WARNING
1270
1271 None at this time.
1272
1273 =head1 NOTES
1274
1275 If the $debug parameter is specified on package new(), no actions are 
1276 performed. Rather they are printed to STDOUT and 0 is returned.
1277
1278 Available Device Handlers:
1279
1280 CDROM_TYPE,
1281 CHANGER_TYPE,
1282 DISK_TYPE,
1283 VDISK_TYPE,
1284 VCDROM_TYPE,
1285 DISKPERF_TYPE,
1286 MODISK_TYPE,
1287 MODISKPERF_TYPE,
1288 TAPE_TYPE,
1289 TAPEPERF_TYPE
1290
1291 To specify a device handler to a method, use the following syntax:
1292
1293 $SCST::SCST::<handler type>
1294
1295 For example:
1296
1297 $SCST::SCST::MODISK_TYPE
1298
1299 =cut