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