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