- Adds read only flag support to assignDeviceToGroup() and
[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.2;
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         my $readOnly = shift;
878
879         $readOnly = 'READ_ONLY' if (defined($readOnly) && $readOnly);
880
881         if (!$self->groupExists($group)) {
882                 $self->{'error'} = "assignDeviceToGroup(): Group '$group' does not exist";
883                 return 1;
884         }
885
886         if ($self->groupDeviceExists($device, $group, $lun)) {
887                 $self->{'error'} = "assignDeviceToGroup(): Device '$device' is already ".
888                   "assigned to group '$group'";
889                 return 2;
890         }
891
892         my $cmd = "add $device $lun $readOnly\n";
893
894         my $rc = $self->group_private($group, $_SCST_DEVICES_IO_, $cmd);
895
896         return 0 if ($self->{'debug'});
897         return $rc if ($rc);
898
899         $rc = !$self->groupDeviceExists($device, $group, $lun);
900
901         if ($rc) {
902                 $self->{'error'} = "assignDeviceToGroup(): An error occured while assigning device '$device' ".
903                   "to group '$group'. See dmesg/kernel log for more information.";
904         }
905
906         return $rc;
907 }
908
909 sub replaceDeviceInGroup {
910         my $self = shift;
911         my $newDevice = shift;
912         my $group = shift;
913         my $lun = shift;
914         my $readOnly = shift;
915
916         $readOnly = 'READ_ONLY' if (defined($readOnly) && $readOnly);
917
918         if (!$self->groupExists($group)) {
919                 $self->{'error'} = "replaceDeviceInGroup(): Group '$group' does not exist";
920                 return 1;
921         }
922
923         if ($self->groupDeviceExists($newDevice, $group, $lun)) {
924                 $self->{'error'} = "replaceDeviceInGroup(): Device '$newDevice' is already ".
925                   "assigned to group '$group'";
926                 return 2;
927         }
928
929         my $cmd = "replace $newDevice $lun $readOnly\n";
930
931         my $rc = $self->group_private($group, $_SCST_DEVICES_IO_, $cmd);
932
933         return 0 if ($self->{'debug'});
934         return $rc if ($rc);
935
936         $rc = !$self->groupDeviceExists($newDevice, $group, $lun);
937
938         if ($rc) {
939                 $self->{'error'} = "replaceDeviceInGroup(): An error occured while replacing lun '$lun' with ".
940                   " device '$newDevice' in group '$group'. See dmesg/kernel log for more information.";
941         }
942
943         return $rc;
944 }
945
946 sub assignDeviceToHandler {
947         my $self = shift;
948         my $device = shift;
949         my $handler = shift;
950         my $handler_io = $_IO_MAP_{$handler};
951         my $handler_name = $_REVERSE_MAP_{$handler};
952
953         if (!$handler_io) {
954                 $self->{'error'} = "assignDeviceToHandler(): Failed to open handler IO '$handler_io' or ".
955                   "handler '$handler_name' ($handler) invalid";
956                 return 1;
957         }
958
959         if (!$self->handlerExists($handler)) {
960                 $self->{'error'} = "assignDeviceToHandler(): Handler '$handler_name' ($handler) does not exist";
961                 return 1;
962         }
963
964         if ($self->handlerDeviceExists($handler, $device)) {
965                 $self->{'error'} = "assignDeviceToHandler(): Device '$device' is already assigned".
966                   " to handler '$handler_name' ($handler)";
967                 return 2;
968         }
969
970         my $cmd = "assign $device $handler_name\n";
971
972         my $rc = $self->scst_private($cmd);
973
974         return 0 if ($self->{'debug'});
975         return $rc if($rc);
976
977         $rc = !$self->handlerDeviceExists($handler, $device);
978
979         if ($rc) {
980                 $self->{'error'} = "assignDeviceToHandler(): An error occured while assigning device '$device' ".
981                   "to handler '$handler_name' ($handler). See dmesg/kernel log for more information.";
982         }
983
984         return $rc;
985 }
986
987 sub removeDeviceFromGroup {
988         my $self = shift;
989         my $device = shift;
990         my $group = shift;
991
992         if (!$self->groupExists($group)) {
993                 $self->{'error'} = "removeDeviceFromGroup(): Group '$group' does not exist";
994                 return 1;
995         }
996
997         if (!$self->groupDeviceExists($device, $group)) {
998                 $self->{'error'} = "removeDeviceFromGroup(): Device '$device' does not exist in group '$group'";
999                 return 2;
1000         }
1001
1002         my $cmd = "del $device\n";
1003
1004         my $rc = $self->group_private($group, $_SCST_DEVICES_IO_, $cmd);
1005
1006         return 0 if ($self->{'debug'});
1007         return $rc if ($rc);
1008
1009         $rc = $self->groupDeviceExists($device, $group);
1010
1011         if ($rc) {
1012                 $self->{'error'} = "removeDeviceFromGroup(): An error occured while removing device '$device' ".
1013                   "from group '$group'. See dmesg/kernel log for more information.";
1014         }
1015
1016         return $rc;
1017 }
1018
1019 sub clearGroupDevices {
1020         my $self = shift;
1021         my $group = shift;
1022
1023         return 1 if (!$self->groupExists($group));
1024
1025         my $cmd = "clear\n";
1026
1027         my $rc = $self->group_private($group, $_SCST_DEVICES_IO_, $cmd);
1028
1029         return 0 if ($self->{'debug'});
1030
1031         if ($rc) {
1032                 $self->{'error'} = "clearGroupDevices(): An error occured while clearing devices from ".
1033                   "group '$group'. See dmesg/kernel log for more information.";
1034                 return $rc;
1035         }
1036
1037         my $devices = $self->groupDevices($group);
1038
1039         return (keys %{$devices});
1040 }
1041
1042 sub handler_private {
1043         my $self = shift;
1044         my $handler_io = shift;
1045         my $cmd = shift;
1046
1047         my $io = new IO::File $handler_io, O_WRONLY;
1048
1049         if (!$io) {
1050                 cluck("WARNING: SCST/SCST.pm: Failed to open handler IO $handler_io, assuming disabled");
1051                 return 0;
1052         }
1053
1054         if ($self->{'debug'}) {
1055                 print "DBG($$): '$handler_io' -> '$cmd'\n";
1056         } else {
1057                 print $io "$cmd\0";
1058         }
1059
1060         close $io;
1061
1062         return 0;
1063 }
1064
1065 sub scst_private {
1066         my $self = shift;
1067         my $cmd = shift;
1068
1069         my $io = new IO::File $_SCST_IO_, O_WRONLY;
1070
1071         if (!$io) {
1072                 $self->{'error'} = "SCST/SCST.pm: Failed to open handler IO '$_SCST_IO_'";
1073                 return 1;
1074         }
1075
1076         if ($self->{'debug'}) {
1077                 print "DBG($$): '$_SCST_IO_' -> '$cmd'\n";
1078         } else {
1079                 print $io "$cmd\0";
1080         }
1081
1082         close $io;
1083
1084         return 0;
1085 }
1086
1087 sub group_private {
1088         my $self = shift;
1089         my $group = shift;
1090         my $file = shift;
1091         my $cmd = shift;
1092
1093         my $io = new IO::File $_SCST_GROUPS_DIR_."/$group/".$file, O_WRONLY;
1094
1095         if (!$io) {
1096                 $self->{'error'} = "SCST/SCST.pm: Failed to open handler IO '".$_SCST_GROUPS_DIR_."/$group/".$file."'";
1097                 return 1;
1098         }
1099
1100         if ($self->{'debug'}) {
1101                 print "DBG($$): $_SCST_GROUPS_DIR_/$group/$file -> $cmd\n";
1102         } else {
1103                 print $io "$cmd\0";
1104         }
1105
1106         close $io;
1107
1108         return 0;
1109 }
1110
1111 sub checkOptions {
1112         my $self = shift;
1113         my $options = shift;
1114         my $o_string;
1115         my $b_string;
1116         my $bad = 0;
1117
1118         return undef, 1 if (!$options);
1119
1120         foreach my $option (split(/[\s+|\,]/, $options)) {
1121                 my $map = $_AVAILABLE_OPTIONS_{$option};
1122
1123                 if (!$map) {
1124                         $bad = 1;
1125                         $b_string .= ",$option";
1126                 } else {
1127                         $o_string .= ",$map";
1128                 }
1129         }
1130
1131         if ($bad) {
1132                 $b_string =~ s/^\,//;
1133                 return $b_string, 0;
1134         }
1135
1136         $o_string =~ s/^\,//;
1137
1138         return $o_string, 1;
1139 }
1140
1141 sub errorString {
1142         my $self = shift;
1143
1144         return undef if (!$self->{'error'});
1145
1146         my $string = $self->{'error'};
1147         $self->{'error'} = undef;
1148
1149         return $string;
1150 }
1151
1152 sub cleanupString {
1153         my $string = shift;
1154
1155         $string =~ s/^\s+//;
1156         $string =~ s/\s+$//;
1157
1158         return $string;
1159 }
1160
1161 ;1 __END__
1162
1163 =head1 NAME
1164
1165 SCST::SCST - Generic SCST methods.
1166
1167 =head1 SYNOPSIS
1168
1169     use SCST::SCST;
1170
1171     $p = SCST::SCST->new();
1172     
1173     print "Using SCST version".$p->scstVersion()."\n";
1174     
1175     if ($p->handlerDeviceExists($SCST::SCST::VDISK_TYPE)) {
1176          print "openDevice() failed\n"
1177            if ($p->openDevice($SCST::SCST::VDISK_TYPE, 'DISK01', '/vdisk/disk01.dsk'));
1178     }
1179     
1180     undef $p;
1181
1182 =head1 DESCRIPTION
1183
1184 Generic SCST methods.
1185
1186 =head2 Methods
1187
1188 =over 5
1189
1190 =item SCST::SCST->new();
1191
1192 Create a new SCST object. If the argument $debug is non-zero no changes
1193 will be made.
1194
1195 Arguments: (bool) $debug
1196
1197 Returns: (object) $new
1198
1199 =item SCST::SCST->scstVersion();
1200
1201 Returns the version of SCST running.
1202
1203 Arguments: void
1204
1205 Returns: (string) $version
1206
1207 =item SCST::SCST->groups();
1208
1209 Returns a list of security groups configured.
1210
1211 Arguments: void
1212
1213 Returns: (array ref) $groups
1214
1215 =item SCST::SCST->groupExists();
1216
1217 Checks for a specified group's existance.
1218
1219 Arguments: (string) $group
1220
1221 Returns: (boolean) $groupExists
1222
1223 =item SCST::SCST->addGroup();
1224
1225 Adds a security group to SCST's configuration. Returns 0 upon success, 1 if
1226 unsuccessfull and  2 if the group already exists.
1227
1228 Arguments: (string) $group
1229
1230 Returns: (int) $success
1231
1232 =item SCST::SCST->removeGroup();
1233
1234 Removes a group from SCST's configuration. Returns 0 upon success, 1 if
1235 unsuccessfull and 2 if group does not exist.
1236
1237 =item SCST::SCST->renameGroup();
1238
1239 Renames an already existing group. Returns 0 upon success, 1 if unsuccessfull
1240 or 2 if the new group name already exists.
1241
1242 =item SCST::SCST->sgvStats();
1243
1244 Returns a hash of stats gathered from /proc/scsi_tgt/sgv.
1245
1246 Arguments: void
1247
1248 Returns: (hash ref) $stats
1249
1250 Hash Layout: See /proc/scsi_tgt/sgv for tokens. This methods simply hashes
1251 what's found there and returns it with no further processing.
1252
1253 =item SCST::SCST->sessions();
1254
1255 Returns a hash of current SCST initiator sessions. 
1256
1257 Arguments: void
1258
1259 Returns: (hash ref) $sessions
1260
1261 Hash Layout: See /proc/scsi_tgt/sessions for tokens. This methods simply hashes
1262 what's found there and returns it with no further processing.
1263
1264 =item SCST::SCST->devices();
1265
1266 Returns a hash of devices configured without regard to device handler.
1267
1268 Arguments: void
1269
1270 Returns: (hash ref) $devices
1271
1272 Hash Layout: (string) $device = (int) $handler
1273
1274 =item SCST::SCST->handlerDevices();
1275
1276 Returns a hash of devices configured for a specified device handler.
1277
1278 Arguments: (int) $handler
1279
1280 Returns: (hash ref) $devices
1281
1282 Hash Layout: (string) $device -> SIZE = (int) $deviceSize
1283              (string) $device -> PATH = (string) $devicePath
1284              (string) $device -> OPTIONS = (string) $options (comma seperated)
1285
1286 =item SCST::SCST->handlerDeviceExists();
1287
1288 Checks for a specified device is configured for a specified device handler.
1289
1290 Arguments: (int) $handler, (string) $device
1291
1292 Returns: (boolean) $deviceExists
1293
1294 =item SCST::SCST->handlerType();
1295
1296 Return the handler type for the specified handler. Handler types are:
1297
1298   SCST::SCST::IOTYPE_PHYSICAL
1299   SCST::SCST::IOTYPE_VIRTUAL
1300   SCST::SCST::IOTYPE_PERFORMANCE
1301
1302 Arguments: (int) $handler
1303
1304 Returns: (int) $handler_type
1305
1306 =item SCST::SCST->openDevice();
1307
1308 Opens an already existing specified device for the specified device handler.
1309 Returns 0 upon success, 1 if unsuccessfull and 2 if the device already exists.
1310
1311 Available options for the parameter $options are: WRITE_THROUGH, READ_ONLY, O_DIRECT
1312
1313 Arguments: (int) $handler, (string) $device, (string) $path [, (string) $options]
1314
1315 Returns: (int) $success
1316
1317 =item SCST::SCST->closeDevice();
1318
1319 Closes an open device configured for the specified device handler. Returns
1320 0 upon success, 1 if unsuccessfull and 2 of the device does not exist.
1321
1322 Arguments: (int) $handler, (string) $device, (string) $path
1323
1324 Returns: (int) $success
1325
1326 =item SCST::SCST->userExists();
1327
1328 Checks for a specified user with the specified security group.
1329
1330 Arguments: (string) $user, (string) $group
1331
1332 Returns (boolean) $userExists
1333
1334 =item SCST::SCST->users();
1335
1336 Returns a list of users configured for a given security group.
1337
1338 Arguments: (string) $group
1339
1340 Returns: (hash ref) $users
1341
1342 =item SCST::SCST->addUser();
1343
1344 Adds the specified user to the specified security group. Returns 0
1345 upon success, 1 if unsuccessfull and 2 if the user already exists.
1346
1347 Arguments: (string) $user, (string) $group
1348
1349 Returns: (int) $success
1350
1351 =item SCST::SCST->removeUser();
1352
1353 Removed the specified user from the specified security group. Returns
1354 0 upon success, 1 if unsuccessfull and 2 if the user does not exist.
1355
1356 Arguments: (string) $user, (string) $group
1357
1358 Returns: (int) $success
1359
1360 =item SCST::SCST->moveUser();
1361
1362 Moves a user from one group to another. Both groups must be defined
1363 and user must already exist in the first group. Returns 0 upon
1364 success, 1 if unsuccessfull and 2 if the user already exists in the
1365 second group.
1366
1367 Arguments: (string) $user, (string) $fromGroup, (string) $toGroup
1368
1369 Returns: (int) $success
1370
1371 =item SCST::SCST->clearUsers();
1372
1373 Removes all users from the specified security group. Returns 0 upon
1374 success or 1 if unsuccessfull.
1375
1376 Arguments: (string) $group
1377
1378 Returns: (int) $success
1379
1380 =item SCST::SCST->handlerExists();
1381
1382 Checks if a specified device handler exists within SCST's configuration.
1383
1384 Arguments: (int) $handler
1385
1386 Returns: (boolean) $handlerExists
1387
1388 =item SCST::SCST->handlers();
1389
1390 Returns a list of configured device handlers.
1391
1392 Arguments: void
1393
1394 Returns: (array ref) $handlers
1395
1396 =item SCST::SCST->groupDeviceExists();
1397
1398 Checks if a specified device is assigned to a specified security group.
1399 If the optional $lun argument is specified, this method also matches
1400 the lun.
1401
1402 Arguments: (string) $device, (string) $group [, (int) $lun]
1403
1404 Returns: (boolean) $deviceExists
1405
1406 =item SCST::SCST->groupDevices();
1407
1408 Returns a hash if devices assigned to the specified security group.
1409
1410 Arguments: (string) $group
1411
1412 Returns: (hash ref) $devices
1413
1414 Hash Layout: (string) $device = (int) $lun
1415
1416 =item SCST::SCST->assignDeviceToGroup();
1417
1418 Assigns the specified device to the specified security group. Returns
1419 0 upon success, 1 if unsuccessfull and 2 if the device has already
1420 been assigned to the specified security group.
1421
1422 Arguments: (string) $device, (string) $group, (int) $lun [, (bool) $rd_only]
1423
1424 Returns: (int) $success
1425
1426 =item SCST::SCST->replaceDeviceInGroup();
1427
1428 Replaces an already assigned device to the specified lun in a
1429 specified security group with $newDevice. Returns 0 upon success, 1
1430 if unsuccessfull and 2 if the device has already been assigned to
1431 the specified security group.
1432
1433 Arguments: (string) $newDevice, (string) $group, (int) $lun [, (bool) $rd_only]
1434
1435 Returns (int) $success
1436
1437 =item SCST::SCST->assignDeviceToHandler();
1438
1439 Assigns specified device to specified handler. Returns 0 upon success,
1440 1 if unsuccessfull and 2 if the specified device is already assigned to
1441 the specified handler.
1442
1443 Arguments: (string) $device, (string) $handler
1444
1445 Returns: (int) $success
1446
1447 =item SCST::SCST->removeDeviceFromGroup();
1448
1449 Removes the specified device from the specified security group. Returns
1450 0 upon success, 1 if unsuccessfull and 2 if the device has not been
1451 assigned to the specified security group.
1452
1453 Arguments: (string) $device, (string) $group
1454
1455 Returns: (int) $success
1456
1457 =item SCST::SCST->clearGroupDevices();
1458
1459 Removes all devices from the specified security group. Returns 0 upon
1460 success or 1 if unsuccessfull.
1461
1462 Arguments: (string) $group
1463
1464 Returns: (int) $success
1465
1466 =item SCST::SCST->errorString();
1467
1468 Contains a description of the last error occured or undef if no error
1469 has occured or if this method has already been called once since the
1470 last error.
1471
1472 Arguments: (void)
1473
1474 Returns: (string) $error_string
1475
1476 =back
1477
1478 =head1 WARNING
1479
1480 None at this time.
1481
1482 =head1 NOTES
1483
1484 If the $debug parameter is specified on package new(), no actions are 
1485 performed. Rather they are printed to STDOUT and 0 is returned.
1486
1487 Available Device Handlers:
1488
1489 CDROM_TYPE,
1490 CHANGER_TYPE,
1491 DISK_TYPE,
1492 VDISK_TYPE,
1493 VCDROM_TYPE,
1494 DISKPERF_TYPE,
1495 MODISK_TYPE,
1496 MODISKPERF_TYPE,
1497 TAPE_TYPE,
1498 TAPEPERF_TYPE
1499
1500 To specify a device handler to a method, use the following syntax:
1501
1502 $SCST::SCST::<handler type>
1503
1504 For example:
1505
1506 $SCST::SCST::MODISK_TYPE
1507
1508 =cut