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