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