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