Whamcloud - gitweb
6a755aaa4825a87109fba5a2dfdeb4615b5e54a0
[fs/lustre-release.git] / lustre / obdclass / obdcontrol
1 #!/usr/bin/perl
2
3 #
4 # This code is issued under the GNU General Public License.
5 # See the file COPYING in this distribution
6 #
7 # Copyright (C) 1998, Stelias Computing
8
9 # Modified for InterMezzo from Gordian's HSM bcache device/jcm module
10 # Copyright (C) 1999, Carnegie Mellon University
11 #
12 # Derived from InterMezzo's incontrol, modified for OBD's
13 # Copyright (C) 1999, Stelias Computing
14 #
15 #
16
17 #use strict;
18 BEGIN { require "asm/errno.ph" };
19 BEGIN { require "asm/ioctl.ph" };
20
21 # p2ph generated invalid macros for ioctl stuff, so I override some of it here
22 eval 'sub OBD_IOC_CREATE () { &_IOC(2, ord(\'f\'), 3, 4);}' unless
23   defined(&OBD_IOC_CREATE);
24 eval 'sub OBD_IOC_SETUP () { &_IOC(1, ord(\'f\'), 4, 4);}' unless
25   defined(&OBD_IOC_SETUP);
26 eval 'sub OBD_IOC_CLEANUP () { &_IOC(0, ord(\'f\'), 5, 0);}' unless
27   defined(&OBD_IOC_CLEANUP);
28 eval 'sub OBD_IOC_DESTROY () { &_IOC(1, ord(\'f\'), 6, 4);}' unless
29   defined(&OBD_IOC_DESTROY);
30 eval 'sub OBD_IOC_PREALLOCATE () { &_IOC(3, ord(\'f\'), 7, 4);}' unless
31   defined(&OBD_IOC_PREALLOCATE);
32 eval 'sub OBD_IOC_DEC_USE_COUNT () { &_IOC(0, ord(\'f\'), 8, 0);}' unless
33   defined(&OBD_IOC_DEC_USE_COUNT);
34 eval 'sub OBD_IOC_SETATTR () { &_IOC(1, ord(\'f\'), 9, 4);}' unless
35   defined(&OBD_IOC_SETATTR);
36 eval 'sub OBD_IOC_GETATTR () { &_IOC(2, ord(\'f\'), 10, 4);}' unless
37   defined(&OBD_IOC_GETATTR);
38 eval 'sub OBD_IOC_READ () { &_IOC(3, ord(\'f\'), 11, 4);}' unless
39   defined(&OBD_IOC_READ);
40 eval 'sub OBD_IOC_WRITE () { &_IOC(3, ord(\'f\'), 12, 4);}' unless
41   defined(&OBD_IOC_WRITE);
42 eval 'sub OBD_IOC_CONNECT () { &_IOC(2, ord(\'f\'), 13, 4);}' unless
43   defined(&OBD_IOC_CONNECT);
44 eval 'sub OBD_IOC_DISCONNECT () { &_IOC(1, ord(\'f\'), 14, 4);}' unless
45   defined(&OBD_IOC_DISCONNECT);
46 eval 'sub OBD_IOC_STATFS () { &_IOC(3, ord(\'f\'), 15, 4);}' unless
47   defined(&OBD_IOC_STATFS);
48 eval 'sub OBD_IOC_SYNC () { &_IOC(2, ord(\'f\'), 16, 4);}' unless
49   defined(&OBD_IOC_SYNC);
50 eval 'sub OBD_IOC_READ2 () { &_IOC(3, ord(\'f\'), 17, 4);}' unless
51   defined(&OBD_IOC_READ2);
52 eval 'sub OBD_IOC_FORMATOBD () { &_IOC(3, ord(\'f\'), 18, 4);}' unless
53   defined(&OBD_IOC_FORMATOBD);
54 eval 'sub OBD_IOC_PARTITION () { &_IOC(3, ord(\'f\'), 19, 4);}' unless
55   defined(&OBD_IOC_PARTITION);
56 eval 'sub OBD_IOC_ATTACH () { &_IOC(3, ord(\'f\'), 20, 4);}' unless
57   defined(&OBD_IOC_ATTACH);
58 eval 'sub OBD_IOC_DETACH () { &_IOC(3, ord(\'f\'), 21, 4);}' unless
59   defined(&OBD_IOC_DETACH);
60 eval 'sub OBD_IOC_COPY () { &_IOC(3, ord(\'f\'), 22, 4);}' unless
61   defined(&OBD_IOC_COPY);
62 eval 'sub OBD_IOC_MIGR () { &_IOC(3, ord(\'f\'), 23, 4);}' unless
63   defined(&OBD_IOC_MIGR);
64 eval 'sub OBD_SNAP_SETTABLE () { &_IOC(3, ord(\'f\'), 40, 4);}' unless
65   defined(&OBD_SNAP_SETTABLE);
66 eval 'sub OBD_SNAP_PRINTTABLE () { &_IOC(3, ord(\'f\'), 41, 4);}' unless
67   defined(&OBD_SNAP_PRINTTABLE);
68 eval 'sub OBD_SNAP_DELETE() { &_IOC(3, ord(\'f\'), 42, 4);}' unless
69   defined(&OBD_SNAP_DELETE);
70 eval 'sub OBD_SNAP_RESTORE() { &_IOC(3, ord(\'f\'), 43, 4);}' unless
71   defined(&OBD_SNAP_RESTORE);
72
73 eval 'sub OBD_EXT2_RUNIT () { &_IOC(3, ord(\'f\'), 61, 4);}' unless
74   defined(&OBD_EXT2_RUNIT);
75
76 eval 'sub ATTR_MODE () {1;}' unless defined(&ATTR_MODE);
77 eval 'sub ATTR_UID () {2;}' unless defined(&ATTR_UID);
78 eval 'sub ATTR_GID () {4;}' unless defined(&ATTR_GID);
79 eval 'sub ATTR_SIZE () {8;}' unless defined(&ATTR_SIZE);
80 eval 'sub ATTR_ATIME () {16;}' unless defined(&ATTR_ATIME);
81 eval 'sub ATTR_MTIME () {32;}' unless defined(&ATTR_MTIME);
82 eval 'sub ATTR_CTIME () {64;}' unless defined(&ATTR_CTIME);
83
84 use Getopt::Long;
85 use File::stat;
86 use Storable;
87 use Carp;
88 use Term::ReadLine;
89 use IO::Handle;
90
91
92 # NOTE long long are layed out in ia32 memory as follows:
93 # u = 0xaaaabbbbccccdddd has ccccdddd at &u and aaaabbbb 4 bytes on
94 # this may be different on other architectures
95
96 # we use 32-bit integers for all 64-bit quantities in this program
97 # #define OBD_INLINESZ  64
98 # #define OBD_OBDMDSZ   64
99 # /* Note: 64-bit types are 64-bit aligned in structure */
100 # struct obdo {
101 #       obd_id                  o_id;
102 #       obd_gr                  o_gr;
103 #       obd_time                o_atime;
104 #       obd_time                o_mtime;
105 #       obd_time                o_ctime;
106 #       obd_size                o_size;
107 #       obd_blocks              o_blocks;
108 #       obd_blksize             o_blksize;
109 #       obd_mode                o_mode;
110 #       obd_uid                 o_uid;
111 #       obd_gid                 o_gid;
112 #       obd_flag                o_flags;
113 #       obd_flag                o_obdflags;
114 #       obd_count               o_nlink;
115 #       obd_flag                o_valid;        /* hot fields in this obdo */
116 #       char                    o_inline[OBD_INLINESZ];
117 #       char                    o_obdmd[OBD_OBDMDSZ];
118 #       struct list_head        o_list;
119 #       struct obd_ops          *o_op;
120 # };
121
122 sub obdo_pack {
123     my $obdo = shift;
124     pack "LL LL LL LL LL LL LL L L L L L L L L a60 a64 L L L", 
125     $obdo->{id}, 0, 
126     $obdo->{gr}, 0, 
127     $obdo->{atime}, 0, 
128     $obdo->{mtime}, 0 ,
129     $obdo->{ctime}, 0, 
130     $obdo->{size}, 0, 
131     $obdo->{blocks}, 0, 
132     $obdo->{blksize},
133     $obdo->{mode},
134     $obdo->{uid},
135     $obdo->{gid},
136     $obdo->{flags},
137     $obdo->{obdflags},
138     $obdo->{nlink},     
139     $obdo->{valid},     
140     $obdo->{inline},
141     $obdo->{obdmd},
142     0, 0, # struct list_head 
143     0;  #  struct obd_ops 
144 }
145
146 sub obdo_unpack {
147     my $buf = shift;
148     my $offset = shift;
149     my $obdo;
150     ($obdo->{id},
151     $obdo->{gr},
152     $obdo->{atime},
153     $obdo->{mtime},
154     $obdo->{ctime},
155     $obdo->{size},
156     $obdo->{blocks},
157     $obdo->{blksize},
158     $obdo->{mode},
159     $obdo->{uid},
160     $obdo->{gid},
161     $obdo->{flags},
162     $obdo->{obdflags},
163     $obdo->{nlink},     
164     $obdo->{valid},     
165     $obdo->{inline},
166     $obdo->{obdmd}) = unpack "x${offset}Lx4 Lx4 Lx4 Lx4 Lx4 Lx4 Lx4 L L L L L L L L a60 a64", $buf;
167     $obdo;
168 }
169
170 sub obdo_print {
171
172     my $obdo = shift;
173
174     printf "id: %d\ngrp: %d\natime: %s\nmtime: %s\nctime: %s\nsize: %d\nblocks: %d\nblksize: %d\nmode: %x\nuid: %d\ngid: %d\nflags: %x\nobdflags: %x\nnlink: %d\nvalid: %x\ninline: %s\nobdmd: %s\n", 
175     $obdo->{id},
176     $obdo->{gr},
177     $obdo->{atime},
178     $obdo->{mtime},
179     $obdo->{ctime},
180     $obdo->{size},
181     $obdo->{blocks},
182     $obdo->{blksize},
183     $obdo->{mode},
184     $obdo->{uid},
185     $obdo->{gid},
186     $obdo->{flags},
187     $obdo->{obdflags},
188     $obdo->{nlink},     
189     $obdo->{valid},     
190     $obdo->{inline},
191     $obdo->{obdmd};
192 }
193
194
195 my ($file);
196
197 GetOptions("f!" => \$file, "device=s" => \$::device, ) || die "Getoptions";
198
199
200 # get a console for the app
201
202 my $line;
203 my $command;
204 my $arg;
205
206 my %commands =
207     ('device' => {func => "Device", doc => "device <dev>: open another OBD device"},
208      'create' => {func => "Create", doc => "create: creates a new inode"},
209      'attach' => {func => "Attach", doc => "attach { ext2_obd | snap_obd snapdev snapidx tableno | scsi_obd adapter bus tid lun }" },
210      'detach' => {func => "Detach", doc => "detach this device"},
211      'testext2iterator' => {func => "TestExt2Iterator", doc => "test ext2 iterator function"},
212      'snapset' => {func => "SnapSetTable", doc => "snapset <tableno> <file>: set the table (created with snaptable) as table #tableno" },
213      'snapprint' => {func => "SnapPrint", doc => "snapprint <tableno>: output the contents of table #tableno to the syslog"},
214      'snapdelete' => {func => "SnapDelete", doc => "snapdelete: delete connected snap obd objects from disk"},
215      'snaprestore' => {func => "SnapRestore", doc => "snaprestore : restore connected old snap objects to be current"},
216      'snaptable' => {func => "SnapShotTable", doc => "snaptable: build a snapshot table (interactive)"},
217      'copy' => {func => "Copy", doc => "copy <srcid> <tgtid>: copy objects"},
218      'migrate' => {func => "Migrate", doc => "migrate <srcid> <tgtid>: migrate data from one object to another"},
219      'format' => {func => "Format", doc => "format type adapter bus tid lun size"},
220      'partition' => {func => "Partition", doc => "partition type adapter bus tid lun partition size"},
221      'setup' => {func => "Setup", doc => "setup [type]: link this OBD device to the underlying device (default type ext2_obd)"},
222      'connect' => {func => "Connect", doc => "connect: allocates client ID for this session"},
223      'disconnect' => {func => "Disconnect", doc => "disconnect [id]: frees client resources"},
224      'sync' => {func => "Sync", doc => "sync: flushes buffers to disk"},
225      'destroy' => {func => "Destroy", doc => "destroy <inode>: destroys an inode"},
226      'cleanup' => {func => "Cleanup", doc => "cleanup the minor obd device"},
227      'dec_use_count' => {func => "Decusecount", doc => "decreases the module use count so that the module can be removed following an oops"},
228      'read' => {func => "Read", doc => "read <inode> <count> [offset]"},
229      'fsread' => {func => "Read2", doc => "read <inode> <count> [offset]"},
230      'write' => {func => "Write", doc => "write <inode> <offset> <text>"},
231      'setattr' => {func => "Setattr", doc => "setattr <inode> [mode [uid [gid [size [atime [mtime [ctime]]]]]]]"},
232      'getattr' => {func => "Getattr", doc => "getattr <inode>: displays inode object attributes"},
233      'preallocate' => {func => "Preallocate", doc => "preallocate [num]: requests preallocation of num inodes."},
234      'statfs' => {func => "Statfs", doc => "statfs: filesystem status information"},
235      'help' => {func => \&Help,  doc => "help: this message"},
236      'quit' => {func => \&Quit,  doc => "see \"exit\""},
237      'exit' => {func => \&Quit,  doc => "see \"quit\""}
238     );
239
240 #
241 #       setup completion function
242 #
243 my @jcm_cmd_list = keys %commands;
244
245 my $term, $attribs;
246
247
248 # Get going....
249
250 Device($::device);
251
252 sub readl {
253     if ( $file ) {
254         my $str = <STDIN>;
255         chop($str);
256         return $str;
257     } else {
258         return $term->readline(@_);
259     }
260 }
261
262
263
264 if ( $file ) {
265     while ( <STDIN> ) {
266         print $_;
267         execute_line($_);
268     }
269     exit 0;
270 } else {
271     $term = new Term::ReadLine 'obdcontrol ';
272     $attribs = $term->Attribs;
273     $attribs->{attempted_completion_function} = \&completeme;
274     $term->ornaments('md,me,,');        # bold face prompt
275     
276     # make sure stdout is not buffered
277     STDOUT->autoflush(1);
278
279
280     # Get on with the show
281     process_line();
282 }
283
284 #------------------------------------------------------------------------------
285 sub completeme {
286     my ($text, $line, $start, $end) = @_;
287     if (substr($line, 0, $start) =~ /^\s*$/) {
288         $attribs->{completion_word} = \@jcm_cmd_list;
289         return $term->completion_matches($text,
290                                          $attribs->{'list_completion_function'});
291     }
292 }
293
294 sub find_command {
295     my $given = shift;
296     my $name;
297     my @completions = completeme($given, $given, 0, length($given));
298     if ($#completions == 0) {
299         $name = shift @completions;
300     }
301
302     return $name;
303 }
304
305 # start making requests
306 sub process_line {
307   foo:
308     $line = $term->readline("obdcontrol > ");
309     execute_line($line);
310     goto foo;
311 }
312
313 sub execute_line {
314     my $line = shift;
315
316     my @arg = split(' ', $line);
317     my $word = shift @arg;
318
319     my $cmd;
320     if ( $file ) {
321         $cmd = $word;
322     } else {
323         $cmd = find_command($word);
324     }
325     unless ($cmd) {
326         printf STDERR "$word: No such command, or not unique.\n";
327         return (-1);
328     }
329
330     if ($cmd eq "help" || $cmd eq "exit" || $cmd eq "quit") {
331         return (&{$commands{$cmd}->{func}}(@arg));
332     }
333
334     # Call the function.
335     return (&{$commands{$cmd}->{func}}(@arg));
336 }
337
338
339 # select the OBD device we talk to
340 sub Device {
341     my $device = shift;
342
343     if ($::client_id) {
344         print "Disconnecting active session ($::client_id)...";
345         Disconnect($::client_id);
346     }
347     if (! $device ) {
348         $device = "/dev/obd0";
349     }
350     $::device = $device;
351     # Open the device, as we need an FD for the ioctl
352     sysopen(DEV_OBD, $device, 0) || die "Cannot open $device";
353     print "Device now $device\n";
354 }
355
356
357
358 sub Attach {
359     my $err = 0;
360     my $type = shift;
361     my $data;
362     my $datalen = 0;
363
364     if ( ! $type ) {
365         print "error: missing type\n";
366 usage:
367         print "usage: attach {ext2_obd | snap_obd | scsi_obd}\n";
368         return;
369     }
370
371     if ($type eq "scsi_obd" ) {
372         my $adapter = shift;
373         my $bus = shift;
374         my $tid = shift;
375         my $lun = shift;
376
377         $data = pack("iiii", $adapter, $bus, $tid, $lun);
378         $datalen = 4 * 4;
379     } elsif ($type eq "snap_obd" ) {
380         my $snapdev = shift;
381         my $snapidx = shift;
382         my $tableno = shift;
383
384         $data = pack("iii", $snapdev, $snapidx, $tableno);
385         $datalen = 3 * 4;
386     } elsif ($type eq "ext2_obd") {
387         1;
388     } else {
389         print "error: unknown attach type $type\n";
390         goto usage;
391     }
392
393     my $len = length($type);
394     my $cl = length($data);
395
396     print "type $type (len $len), datalen $datalen ($cl)\n";
397     my $packed = pack("Lipip", $::client_id, length($type), $type, $datalen, $data);
398
399     my $rc = ioctl(DEV_OBD, &OBD_IOC_ATTACH, $packed);
400
401     if (!defined $rc) {
402         print STDERR "ioctl failed: $!\n";
403     } elsif ($rc eq "0 but true") {
404         print "Finished (success)\n";
405     } else {
406         print "ioctl returned error code $rc.\n";
407     }
408 }
409
410 sub Detach {
411     my $err = 0;
412     my $data = "";
413     my $rc = ioctl(DEV_OBD, &OBD_IOC_DETACH, $data);
414
415     if (!defined $rc) {
416         print STDERR "ioctl failed: $!\n";
417     } elsif ($rc eq "0 but true") {
418         print "Finished (success)\n";
419     } else {
420         print "ioctl returned error code $rc.\n";
421     }
422 }
423
424
425 sub TestExt2Iterator { 
426     if (!defined($::client_id)) {
427         print "You must first ``connect''.\n";
428         return;
429     }
430
431     my $err = 0;
432     my $type = "ext2_obd";
433  
434     $data = pack("i", 4711); # bogus data
435     $datalen = 4;
436
437     my $len = length($type);
438     my $cl = length($data);
439     my $add = pack("p", $data);
440     print "type $type (len $len), datalen $datalen ($cl)\n";
441     my $packed = pack("Lipip", $::client_id, length($type), $type, $datalen, $data);
442
443     my $rc = ioctl(DEV_OBD, &OBD_EXT2_RUNIT, $packed);
444
445     if (!defined $rc) {
446         print STDERR "ioctl failed: $!\n";
447     } elsif ($rc eq "0 but true") {
448         print "Finished (success)\n";
449     } else {
450         print "ioctl returned error code $rc.\n";
451     }
452 }
453
454
455 sub SnapDelete { 
456     if (!defined($::client_id)) {
457         print "You must first ``connect''.\n";
458         return;
459     }
460
461     my $err = 0;
462     my $type = "snap_obd";
463  
464     $data = pack("i", 4711); # bogus data
465     $datalen = 4;
466
467     my $len = length($type);
468     my $cl = length($data);
469     my $add = pack("p", $data);
470     print "type $type (len $len), datalen $datalen ($cl)\n";
471     my $packed = pack("Lipip", $::client_id, length($type), $type, $datalen, $data);
472
473     # XXX We need to fix this up so that after the objects in this snapshot
474     #     are deleted, the snapshot itself is also removed from the table.
475     my $rc = ioctl(DEV_OBD, &OBD_SNAP_DELETE, $packed);
476
477     if (!defined $rc) {
478         print STDERR "ioctl failed: $!\n";
479     } elsif ($rc eq "0 but true") {
480         print "Finished (success)\n";
481     } else {
482         print "ioctl returned error code $rc.\n";
483     }
484 }
485
486
487 #      this routine does the whole job
488 sub SnapRestore { 
489     my $restoreto = shift;
490     my $snaptable = shift;
491     my $tableno = shift;
492     my $restoretime;
493
494     # don't do anything until connected
495     if (!defined($::client_id)) {
496         print "You must first ``connect''.\n";
497         return;
498     }
499
500     if ( ! $snaptable || ! defined $restoreto ) {
501         print "Usage: snaprestore \"restore to slot\" \"snaptable\" \"tableno\"\n";
502         return;
503     }
504
505     if ( ! -f $snaptable ) {
506         print "Table $snaptable doesn't exist\n";
507         return;
508     }
509    
510     my $table = ReadSnapShotTable($snaptable);
511     $restoretime = FindSnapInTable($table, $restoreto);
512     if ( ! defined $table->{0} || ! defined $restoretime ) {
513         PrintSnapShotTable($table);
514         print "No current or $restoreto slot in this table\n";
515         return;
516     }
517
518     my $currentindex = $table->{0};
519     if (  $table->{$restoretime} == $currentindex ) {
520         print "You should not restore to the current snapshot\n";
521         return;
522     }
523     
524     # swap the entries for 0 and $restoreto
525     my $tmp = $table->{$restoretime};
526     $table->{$restoretime} = $table->{0};
527     $table->{0} = $tmp;
528     # PrintSnapShotTable($table);
529
530     # write it back
531     WriteSnapShotTable($snaptable, $table);
532
533     # set it in the kernel
534     SnapSetTable($tableno, $snaptable);
535
536     # ready for the ioctl
537     my $err = 0;
538     my $type = "snap_obd";
539     $data = pack("i", $currentindex); # slot of previous current snapshot 
540     $datalen = 4;
541
542     my $len = length($type);
543     my $cl = length($data);
544     my $packed = pack("Lipip", $::client_id, length($type), $type, $datalen, $data);
545
546     my $rc = ioctl(DEV_OBD, &OBD_SNAP_RESTORE, $packed);
547
548     if (!defined $rc) {
549         print STDERR "ioctl failed: $!\n";
550     } elsif ($rc eq "0 but true") {
551         print "Snaprestore finished (success)\n";
552         delete $table->{$restoretime} if defined $restoretime;
553         # write it back
554         WriteSnapShotTable($snaptable, $table);
555         
556         # set it in the kernel
557         SnapSetTable($tableno, $snaptable);
558         # PrintSnapShotTable($table);
559
560     } else {
561         print "ioctl returned error code $rc.\n";
562     }
563 }
564
565 sub FindSnapInTable { 
566     my $table = shift;
567     my $snapno =shift;
568
569     foreach my $restoretime ( keys %{$table} ) {
570         if ( $table->{$restoretime} == $snapno) { 
571             print "Found key $restoretime for snapno $snapno\n";
572             return $restoretime;
573         }
574     }
575     undef;
576 }
577             
578
579 sub SnapPrint { 
580     my $err = 0;
581     my $type = "snap_obd";
582     my $snaptableno = shift;
583
584     $data = pack("i", $snaptableno);
585     $datalen = 4;
586
587     my $len = length($type);
588     my $cl = length($data);
589     my $add = pack("p", $data);
590     print "type $type (len $len), datalen $datalen ($cl)\n";
591     my $packed = pack("Lipip", $::client_id, length($type), $type, $datalen, $data);
592
593     my $rc = ioctl(DEV_OBD, &OBD_SNAP_PRINTTABLE, $packed);
594
595     if (!defined $rc) {
596         print STDERR "ioctl failed: $!\n";
597     } elsif ($rc eq "0 but true") {
598         print "Finished (success)\n";
599     } else {
600         print "ioctl returned error code $rc.\n";
601     }
602 }
603
604 sub SnapSetTable {
605     my $err = 0;
606     my $type = "snap_obd";
607     my $snaptableno = shift;
608     my $file = shift;
609     my $snapcount;
610     my $table = {};
611     my $data;
612     my $datalen = 0;
613
614     if ( ! -f $file ) {
615         print "No such file $file\n";
616     }
617
618     $table = ReadSnapShotTable($file);
619
620     $snapcount = keys %{$table};
621     print "Snapcount $snapcount\n";
622
623     if ( ! defined $table->{0} ) {
624         print "No current snapshot in table! First make one\n";
625         return ;
626     }
627     $data = pack("ii", $snaptableno, $snapcount);
628     $datalen = 2 * 4;
629     foreach my $time (sort keys %{$table}) {
630         $data .= pack("Ii", $time, $table->{$time});
631         $datalen += 8;
632     }
633
634     my $len = length($type);
635     my $cl = length($data);
636     my $add = pack("p", $data);
637     print "type $type (len $len), datalen $datalen ($cl)\n";
638     my $packed = pack("Lipip", $::client_id, length($type), $type, $datalen, $data);
639
640     my $rc = ioctl(DEV_OBD, &OBD_SNAP_SETTABLE, $packed);
641
642     if (!defined $rc) {
643         print STDERR "ioctl failed: $!\n";
644     } elsif ($rc eq "0 but true") {
645         print "Finished (success)\n";
646     } else {
647         print "ioctl returned error code $rc.\n";
648     }
649 }
650
651
652 sub SnapShotTable  {
653
654     my $file = &readl("enter file name: ");
655     if ( ! -f $file ) {
656         `touch $file`;
657     }
658     my $table = ReadSnapShotTable($file);
659   
660   again:
661     PrintSnapShotTable($table);
662     my $action = &readl("Add, Delete or Quit [adq]: ");
663     goto done if ($action  =~ "^q.*" );
664     goto add if ($action =~ "^a.*");
665     goto del  if ($action =~ "^d.*");
666     goto again;
667
668   add:
669     my $idx = &readl("enter index where you want this snapshot: ");
670     my $time = &readl("enter time or 'now' or 'current': ");
671     my $oldtime = SnapFindTimeFromIdx($idx, $table);
672     if (defined $oldtime) {
673         print "This already exists, first clean up\n";
674         goto again;
675     }
676
677     if ( $time  eq 'now' ) {
678         $time = time;
679     } elsif ( $time eq 'current' ) { 
680         $time = 0;
681     }
682     $table->{$time} = $idx;
683     goto again;
684
685   del:
686     $didx = &readl("Enter index to delete: ");
687     my $deltime = SnapFindTimeFromIdx($didx, $table);
688     delete $table->{$deltime} if defined $deltime;
689     goto again;
690
691   done:
692     my $ok = &readl("OK with new table? [Yn]: ");
693     unless ( $ok eq "n" )  {
694         WriteSnapShotTable($file, $table);
695     }
696 }
697
698 sub SnapFindTimeFromIdx {
699     my $idx = shift;
700     my $table = shift;
701
702     foreach my $time ( keys %{$table} ) {
703         if ( $table->{$time} == $idx ) {
704             return $time;
705         }
706     }
707     undef;
708 }
709
710 sub PrintSnapShotTable {
711     my $table = shift;
712     my $time;
713     
714     foreach  $time ( sort keys %{$table} ) {
715         my $stime = localtime($time);
716         if ( ! $time ) { 
717             $stime = "current";
718         }
719         printf "Time: %s -- Index %d\n", $stime, $table->{$time};
720     }
721 }
722
723 sub ReadSnapShotTable {
724
725     my $file = shift;
726     my $table = {};
727
728     open FH, "<$file";
729     while ( <FH> ) {
730         my ($time, $index) = split ;
731         $table->{$time} = $index;
732     }
733     close FH;
734
735     PrintSnapShotTable($table);
736
737     return $table;
738 }
739
740 sub WriteSnapShotTable {
741     my $file = shift;
742     my $table = shift;
743
744     open FH, ">$file";
745     foreach my $time ( sort keys %{$table}  ) {
746         print FH "$time $table->{$time}\n";
747     }
748     close FH;
749 }
750
751 sub Copy {
752     my $err = 0;
753     my $srcid = shift;
754     my $dstid = shift;
755
756     # Note: _copy IOCTL takes parameters as dst, src.
757     #       Copy function takes parameters as src, dst.
758     my $data = pack("III", $::client_id, $dstid, $srcid);
759     my $datalen = 12;
760
761     my $packed = pack("ip", $datalen, $data);
762     my $rc = ioctl(DEV_OBD, &OBD_IOC_COPY, $packed);
763
764     if (!defined $rc) {
765         print STDERR "ioctl failed: $!\n";
766     } elsif ($rc eq "0 but true") {
767         print "Finished (success)\n";
768     } else {
769         print "ioctl returned error code $rc.\n";
770     }
771 }
772
773 sub Migrate {
774     my $err = 0;
775     my $srcid = shift;
776     my $dstid = shift;
777
778     # Note: _migr IOCTL takes parameters as dst, src.
779     #       Migrate function takes parameters as src, dst.
780     my $data = pack("III", $::client_id, $dstid, $srcid);
781     my $datalen = 12;
782
783     my $packed = pack("ip", $datalen, $data);
784     my $rc = ioctl(DEV_OBD, &OBD_IOC_MIGR, $packed);
785
786     if (!defined $rc) {
787         print STDERR "ioctl failed: $!\n";
788     } elsif ($rc eq "0 but true") {
789         print "Finished (success)\n";
790     } else {
791         print "ioctl returned error code $rc.\n";
792     }
793 }
794
795
796 sub Format {
797     my $err = 0;
798     my $size = shift;
799     my $data = pack("i", $size);
800     my $datalen = 4;
801
802     my $packed = pack("ip", $datalen, $data);
803     my $rc = ioctl(DEV_OBD, &OBD_IOC_FORMATOBD, $packed);
804
805     if (!defined $rc) {
806         print STDERR "ioctl failed: $!\n";
807     } elsif ($rc eq "0 but true") {
808         print "Finished (success)\n";
809     } else {
810         print "ioctl returned error code $rc.\n";
811     }
812 }
813
814 sub Partition {
815     my $err = 0;
816     my $partno = shift;
817     my $size = shift;
818     my $data = pack("ii", $partno, $size);
819     my $datalen = 2 * 4;
820
821     my $packed = pack("ip", $datalen, $data);
822     my $rc = ioctl(DEV_OBD, &OBD_IOC_PARTITION, $packed);
823
824     if (!defined $rc) {
825         print STDERR "ioctl failed: $!\n";
826     } elsif ($rc eq "0 but true") {
827         print "Finished (success)\n";
828     } else {
829         print "ioctl returned error code $rc.\n";
830     }
831 }
832
833 sub Setup {
834     my $err = 0;
835     my $arg = shift;
836     my $data;
837     my $datalen = 0;
838
839     # XXX we need a getinfo ioctl to validate parameters 
840     # by type here
841
842     if ($arg  && !defined($::st = stat($arg))) {
843             print "$arg is not a valid device\n";
844             return;
845     }
846     
847     if ( $arg ) {
848         $dev = $::st->rdev() unless $dev;
849         $data = pack("i", $dev);
850         $datalen = 4;
851     }
852
853     my $packed = pack("ip", $datalen, $data);
854     my $rc = ioctl(DEV_OBD, &OBD_IOC_SETUP, $packed);
855
856     if (!defined $rc) {
857         print STDERR "ioctl failed: $!\n";
858     } elsif ($rc eq "0 but true") {
859         print "Finished (success)\n";
860     } else {
861         print "ioctl returned error code $rc.\n";
862     }
863 }
864
865 sub Cleanup {
866     my $err = "0";
867     my $rc = ioctl(DEV_OBD, &OBD_IOC_CLEANUP, $err);
868
869     if (!defined $rc) {
870         print STDERR "ioctl failed: $!\n";
871     } elsif ($rc eq "0 but true") {
872         print "Finished (success)\n";
873         $::client_id = 0;
874     } else {
875         print "ioctl returned error code $rc.\n";
876     }
877 }
878
879
880 sub Connect {
881     my $rc;
882
883     my $packed = "";
884     $rc = ioctl(DEV_OBD, &OBD_IOC_CONNECT, $packed);
885     $id = unpack("I", $packed);
886
887     if (!defined $rc) {
888         print STDERR "ioctl failed: $!\n";
889     } elsif ($rc eq "0 but true") {
890         $::client_id = $id;
891         print "Client ID     : $id\n";
892         print "Finished (success)\n";
893     } else {
894         print "ioctl returned error code $rc.\n";
895     }
896 }
897
898 sub Disconnect {
899     my $id = shift;
900
901     if (!defined($id)) {
902         $id = $::client_id;
903     }
904
905     if (!defined($id)) {
906         print "syntax: disconnect [client ID]\n";
907         print "When client ID is not given, the last valid client ID to be returned by a\n";
908         print "connect command this session is used; there is no such ID.\n";
909         return;
910     }
911
912     my $packed = pack("L", $id);
913     my $rc = ioctl(DEV_OBD, &OBD_IOC_DISCONNECT, $packed);
914
915     if (!defined $rc) {
916         print STDERR "ioctl failed: $!\n";
917     } elsif ($rc eq "0 but true") {
918         $::client_id = undef;
919         print "Finished (success)\n";
920     } else {
921         print "ioctl returned error code $rc.\n";
922     }
923 }
924
925 sub Create {
926     my $arg = shift;
927     my $quiet = shift;
928     my $rc;
929     my $prealloc = 0;
930
931     if (defined($quiet) && $quiet ne "quiet") {
932         print "syntax: create [number of objects [quiet]]\n";
933         return;
934     }
935
936     my $packed = pack("IL", $::client_id, $prealloc);
937     if (!defined($arg) || scalar($arg) < 2) {
938         print "Creating 1 object...\n";
939         $rc = ioctl(DEV_OBD, &OBD_IOC_CREATE, $packed);
940         if (!defined($quiet)) {
941             my $ino = unpack("L", $packed);
942             print "Created object #$ino.\n";
943         }
944     } else {
945         my $i;
946
947         print "Creating " . scalar($arg) . " objects...\n";
948         for ($i = 0; $i < scalar($arg); $i++) {
949             $rc = ioctl(DEV_OBD, &OBD_IOC_CREATE, $packed);
950             my $ino = unpack("L", $packed);
951             if ($rc ne "0 but true") {
952                 last;
953                 $packed = pack("IL", $::client_id, $prealloc);
954             } elsif (!defined($quiet)) {
955                 $packed = pack("IL", $::client_id, $prealloc);
956                 print "Created object #$ino.\n";
957             }
958         }
959     }
960
961     if (!defined $rc) {
962         print STDERR "ioctl failed: $!\n";
963     } elsif ($rc eq "0 but true") {
964         print "Finished (success)\n";
965     } else {
966         print "ioctl returned error code $rc.\n";
967     }
968 }
969
970 sub Sync {
971     my $err = "0";
972     my $rc = ioctl(DEV_OBD, &OBD_IOC_SYNC, $err);
973
974     if (!defined $rc) {
975         print STDERR "ioctl failed: $!\n";
976     } elsif ($rc eq "0 but true") {
977         print "Finished (success)\n";
978     } else {
979         print "ioctl returned error code $rc.\n";
980     }
981 }
982
983 sub Destroy {
984     if (!defined($::client_id)) {
985         print "You must first ``connect''.\n";
986         return;
987     }
988
989     my $arg = shift;
990
991     if (!defined($arg) || scalar($arg) < 1) {
992         print "destroy requires the object number to destroy.\n";
993         return;
994     }
995
996     print "Destroying object $arg...\n";
997     my $packed = pack("IL", $::client_id, $arg);
998     my $rc = ioctl(DEV_OBD, &OBD_IOC_DESTROY, $packed);
999
1000     if (!defined $rc) {
1001         print STDERR "ioctl failed: $!\n";
1002     } elsif ($rc eq "0 but true") {
1003         print "Finished (success)\n";
1004     } else {
1005         print "ioctl returned error code $rc.\n";
1006     }
1007 }
1008
1009 sub Getattr {
1010     if (!defined($::client_id)) {
1011         print "You must first ``connect''.\n";
1012         return;
1013     }
1014
1015     my $inode = shift;
1016
1017     if (!defined($inode) || scalar($inode) < 1) {
1018         print "invalid arguments; type \"help getattr\" for a synopsis\n";
1019         return;
1020     }
1021
1022     # see Setattr
1023     my $obdo;
1024     $obdo->{id} = $inode;
1025     my $packed = pack("L", $::client_id) . obdo_pack($obdo);
1026     my $rc = ioctl(DEV_OBD, &OBD_IOC_GETATTR, $packed);
1027
1028     
1029     if (!defined $rc) {
1030         print STDERR "ioctl failed: $!\n";
1031     } elsif ($rc eq "0 but true") {
1032         $obdo = obdo_unpack($packed,  4); 
1033         obdo_print($obdo);
1034     } else {
1035         print "ioctl returned error code $rc.\n";
1036     }
1037 }
1038
1039 sub Setattr {
1040     if (!defined($::client_id)) {
1041         print "You must first ``connect''.\n";
1042         return;
1043     }
1044
1045     my $inode = shift;
1046     my $valid = 0;
1047     my $mode = oct(shift);
1048     my $uid = shift;
1049     my $gid = shift;
1050     my $size = shift;
1051     my $atime = shift;
1052     my $mtime = shift;
1053     my $ctime = shift;
1054
1055     if (defined($uid)) {
1056         $valid |= &ATTR_UID;
1057     }
1058     if (defined($gid)) {
1059         $valid |= &ATTR_GID;
1060     }
1061     if (defined($size)) {
1062         $valid |= &ATTR_SIZE;
1063     }
1064     if (defined($atime)) {
1065         $valid |= &ATTR_ATIME;
1066     }
1067     if (defined($mtime)) {
1068         $valid |= &ATTR_MTIME;
1069     }
1070     if (defined($ctime)) {
1071         $valid |= &ATTR_CTIME;
1072     }
1073     if (defined($mode)) {
1074         $valid |= &ATTR_MODE;
1075     }
1076
1077     if (!defined($inode) || scalar($inode) < 1) {
1078         print "invalid arguments; type \"help setattr\" for a synopsis\n";
1079         return;
1080     }
1081
1082     #struct iattr {
1083     #        unsigned int    ia_valid; (32)
1084     #        umode_t         ia_mode; (16)
1085     #        uid_t           ia_uid; (16)
1086     #        gid_t           ia_gid; (16)
1087     # -- 16 bit alignment here! --
1088     #        off_t           ia_size; (32)
1089     #        time_t          ia_atime; (32)
1090     #        time_t          ia_mtime; (32)
1091     #        time_t          ia_ctime; (32)
1092     #        unsigned int    ia_attr_flags; (32)
1093     #};
1094
1095     printf "valid is %x, mode is %o\n", $valid, $mode;
1096     my $packed = pack("ILLSssx2ILLLL", $::client_id, $inode, $valid, $mode,
1097                       $uid, $gid, $size, $atime, $mtime, $ctime, 0);
1098     my $rc = ioctl(DEV_OBD, &OBD_IOC_SETATTR, $packed);
1099
1100     if (!defined $rc) {
1101         print STDERR "ioctl failed: $!\n";
1102     } elsif ($rc eq "0 but true") {
1103         print "Finished (success)\n";
1104     } else {
1105         print "ioctl returned error code $rc.\n";
1106     }
1107 }
1108
1109 sub Read {
1110     if (!defined($::client_id)) {
1111         print "You must first ``connect''.\n";
1112         return;
1113     }
1114
1115     my $inode = shift;
1116     my $count = shift;
1117     my $offset = shift;
1118   
1119     if (!defined($inode) || scalar($inode) < 1 || !defined($count) ||
1120         $count < 1 || (defined($offset) && $offset < 0)) {
1121         print "invalid arguments; type \"help read\" for a synopsis\n";
1122         return;
1123     }
1124
1125     if (!defined($offset)) {
1126         $offset = 0;
1127     }
1128
1129     print("Reading $count bytes starting at byte $offset from object " .
1130           "$inode...\n");
1131
1132     # "allocate" a large enough buffer
1133     my $buf = sprintf("%${count}s", " ");
1134     die "suck" if (length($buf) != $count);
1135
1136     # the perl we're using doesn't support pack type Q, and offset is 64 bits
1137     my $packed = pack("ILpLLL", $::client_id, $inode, $buf, $count, $offset, 0);
1138
1139     my $rc = ioctl(DEV_OBD, &OBD_IOC_READ, $packed);
1140
1141     $retval = unpack("l", $packed);
1142
1143     if (!defined $rc) {
1144         print STDERR "ioctl failed: $!\n";
1145     } elsif ($rc eq "0 but true") {
1146         if ($retval >= 0) {
1147                 print substr($buf, 0, $retval);
1148                 print "\nRead $retval of an attempted $count bytes.\n";
1149                 print "Finished (success)\n";
1150         } else {
1151                 print "Finished (error $retval)\n";
1152         }
1153     } else {
1154         print "ioctl returned error code $rc.\n";
1155     }
1156 }
1157
1158 sub Read2 {
1159     if (!defined($::client_id)) {
1160         print "You must first ``connect''.\n";
1161         return;
1162     }
1163
1164     my $inode = shift;
1165     my $count = shift;
1166     my $offset = shift;
1167   
1168     if (!defined($inode) || scalar($inode) < 1 || !defined($count) ||
1169         $count < 1 || (defined($offset) && $offset < 0)) {
1170         print "invalid arguments; type \"help read\" for a synopsis\n";
1171         return;
1172     }
1173
1174     if (!defined($offset)) {
1175         $offset = 0;
1176     }
1177
1178     print("Reading $count bytes starting at byte $offset from object " .
1179           "$inode...\n");
1180
1181     # "allocate" a large enough buffer
1182     my $buf = sprintf("%${count}s", " ");
1183     die "suck" if (length($buf) != $count);
1184
1185     # the perl we're using doesn't support pack type Q, and offset is 64 bits
1186     my $packed = pack("ILpLLL", $::client_id, $inode, $buf, $count, $offset, 0);
1187
1188     my $rc = ioctl(DEV_OBD, &OBD_IOC_READ2, $packed);
1189
1190     $retval = unpack("l", $packed);
1191
1192     if (!defined $rc) {
1193         print STDERR "ioctl failed: $!\n";
1194     } elsif ($rc eq "0 but true") {
1195         if ($retval >= 0) {
1196                 print substr($buf, 0, $retval);
1197                 print "\nRead $retval of an attempted $count bytes.\n";
1198                 print "Finished (success)\n";
1199         } else {
1200                 print "Finished (error $retval)\n";
1201         }
1202     } else {
1203         print "ioctl returned error code $rc.\n";
1204     }
1205 }
1206
1207 sub Write {
1208     if (!defined($::client_id)) {
1209         print "You must first ``connect''.\n";
1210         return;
1211     }
1212
1213     my $inode = shift;
1214     my $offset = shift;
1215     my $text = join(' ', @_);
1216     my $count = length($text);
1217
1218     if (!defined($inode) || scalar($inode) < 1 || !defined($offset) ||
1219         scalar($offset) < 0) {
1220         print "invalid arguments; type \"help write\" for a synopsis\n";
1221         return;
1222     }
1223
1224     if (!defined($text)) {
1225         $text = "";
1226         $count = 0;
1227     }
1228
1229     print("Writing $count bytes starting at byte $offset to object " .
1230           "$inode...\n");
1231
1232     # the perl we're using doesn't support pack type Q
1233     my $packed = pack("ILpLLL", $::client_id, $inode, $text, $count, $offset, 0);
1234     my $rc = ioctl(DEV_OBD, &OBD_IOC_WRITE, $packed);
1235
1236     $retval = unpack("l", $packed);
1237
1238     if (!defined $rc) {
1239         print STDERR "ioctl failed: $!\n";
1240     } elsif ($rc eq "0 but true") {
1241         if ($retval >= 0) {
1242                 print "\nWrote $retval of an attempted $count bytes.\n";
1243                 print "Finished (success)\n";
1244         } else {
1245                 print "Finished (error $retval)\n";
1246         }
1247     } else {
1248         print "ioctl returned error code $rc.\n";
1249     }
1250 }
1251
1252 sub Preallocate {
1253     my $arg = shift;
1254
1255     if (!defined($::client_id)) {
1256         print "You must first ``connect''.\n";
1257         return;
1258     }
1259
1260     if (!defined($arg) || scalar($arg) < 1 || scalar($arg) > 32) {
1261         $arg = 32;
1262     }
1263
1264     print "Preallocating $arg inodes...\n";
1265     my $packed = pack("LLx128", $::client_id, $arg);
1266     # client id, alloc, inodes[32]
1267
1268     my $rc = ioctl(DEV_OBD, &OBD_IOC_PREALLOCATE, $packed);
1269
1270     if (!defined $rc) {
1271         print STDERR "ioctl failed: $!\n";
1272     } elsif ($rc eq "0 but true") {
1273         my $alloc = unpack("x4L", $packed);
1274         my @inodes = unpack("x8L32", $packed);
1275         my $i;
1276
1277         print "Got $alloc inodes: ";
1278         foreach $i (@inodes) {
1279             print $i . " ";
1280         }
1281         print "\nFinished (success)\n";
1282     } else {
1283         print "ioctl returned error code $rc.\n";
1284     }
1285 }
1286
1287 sub Decusecount {
1288     my $rc = ioctl(DEV_OBD, &OBD_IOC_DEC_USE_COUNT, 0);
1289
1290     if (!defined $rc) {
1291         print STDERR "ioctl failed: $!\n";
1292     } elsif ($rc eq "0 but true") {
1293         print "Finished (success)\n";
1294     } else {
1295         print "ioctl returned error code $rc.\n";
1296     }
1297 }
1298
1299 sub Statfs {
1300     if (!defined($::client_id)) {
1301         print "You must first ``connect''.\n";
1302         return;
1303     }
1304
1305     # struct statfs {
1306     #         long f_type;
1307     #         long f_bsize;
1308     #         long f_blocks;
1309     #         long f_bfree;
1310     #         long f_bavail;
1311     #         long f_files;
1312     #         long f_ffree;
1313     #         __kernel_fsid_t f_fsid; (64 bits)
1314     #         long f_namelen;
1315     #         long f_spare[6];
1316     # };
1317
1318     my $packed = pack("LLLLLLLIILL6", $::client_id, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1319                       0, 0, 0, 0, 0, 0);
1320
1321     my $rc = ioctl(DEV_OBD, &OBD_IOC_STATFS, $packed);
1322
1323     if (!defined $rc) {
1324         print STDERR "ioctl failed: $!\n";
1325     } elsif ($rc eq "0 but true") {
1326         # skip both the conn_id and the fs_type in the buffer
1327         my ($bsize, $blocks, $bfree, $bavail, $files, $ffree) =
1328             unpack("x4x4LLLLLL", $packed);
1329         print("$bsize byte blocks: $blocks, " . ($blocks - $bfree) . " used, " .
1330               "$bfree free ($bavail available).\n");
1331         print "$files files, " . ($files - $ffree) . " used, $ffree free.\n";
1332         print "Finished (success)\n";
1333     } else {
1334         print "ioctl returned error code $rc.\n";
1335     }
1336 }
1337
1338 sub Help {
1339     my $arg = shift;
1340
1341     if ( !$arg || !$commands{$arg} ) {
1342         print "Comands: ", join( ' ', @jcm_cmd_list), "\n";
1343     } else {
1344         print "Usage: " .  $commands{$arg}->{doc} . "\n";
1345     }
1346 }
1347
1348 sub Quit {
1349     if ($::client_id) {
1350         print "Disconnecting active session ($::client_id)...";
1351         Disconnect($::client_id);
1352     }
1353     exit;
1354 }