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