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