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