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