Whamcloud - gitweb
obdsyncd: removed from CVS
[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 sub Attach {
362     my $err = 0;
363     my $type = shift;
364     my $data;
365     my $datalen = 0;
366
367     if ( ! $type ) {
368         print "error: missing type\n";
369 usage:
370         print "usage: attach {ext2_obd | snap_obd | scsi_obd}\n";
371         return;
372     }
373
374     if ($type eq "scsi_obd" ) {
375         my $adapter = shift;
376         my $bus = shift;
377         my $tid = shift;
378         my $lun = shift;
379
380         $data = pack("iiii", $adapter, $bus, $tid, $lun);
381         $datalen = 4 * 4;
382     } elsif ($type eq "snap_obd" ) {
383         my $snapdev = shift;
384         my $snapidx = shift;
385         my $tableno = shift;
386
387         $data = pack("iii", $snapdev, $snapidx, $tableno);
388         $datalen = 3 * 4;
389     } elsif ($type eq "ext2_obd") {
390         $data = pack("i", 4711);   # bogus data
391         $datalen = 0;
392     } else {
393         print "error: unknown attach type $type\n";
394         goto usage;
395     }
396
397     my $len = length($type);
398     my $cl = length($data);
399
400     print "type $type (len $len), datalen $datalen ($cl)\n";
401     my $packed = pack("Lipip", $::client_id, length($type), $type, $datalen, $data);
402
403     my $rc = ioctl(DEV_OBD, &OBD_IOC_ATTACH, $packed);
404
405     if (!defined $rc) {
406         print STDERR "ioctl failed: $!\n";
407     } elsif ($rc eq "0 but true") {
408         print "Finished (success)\n";
409     } else {
410         print "ioctl returned error code $rc.\n";
411     }
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     print "type $type (len $len), datalen $datalen ($cl)\n";
445     my $packed = pack("Lipip", $::client_id, length($type), $type, $datalen, $data);
446
447     my $rc = ioctl(DEV_OBD, &OBD_EXT2_RUNIT, $packed);
448
449     if (!defined $rc) {
450         print STDERR "ioctl failed: $!\n";
451     } elsif ($rc eq "0 but true") {
452         print "Finished (success)\n";
453     } else {
454         print "ioctl returned error code $rc.\n";
455     }
456 }
457
458
459 sub SnapDelete { 
460     if (!defined($::client_id)) {
461         print "You must first ``connect''.\n";
462         return;
463     }
464
465     my $err = 0;
466     my $type = "snap_obd";
467  
468     $data = pack("i", 4711); # bogus data
469     $datalen = 4;
470
471     my $len = length($type);
472     my $cl = length($data);
473     print "type $type (len $len), datalen $datalen ($cl)\n";
474     my $packed = pack("Lipip", $::client_id, length($type), $type, $datalen, $data);
475
476     # XXX We need to fix this up so that after the objects in this snapshot
477     #     are deleted, the snapshot itself is also removed from the table.
478     my $rc = ioctl(DEV_OBD, &OBD_SNAP_DELETE, $packed);
479
480     if (!defined $rc) {
481         print STDERR "ioctl failed: $!\n";
482     } elsif ($rc eq "0 but true") {
483         print "Finished (success)\n";
484     } else {
485         print "ioctl returned error code $rc.\n";
486     }
487 }
488
489
490 #      this routine does the whole job
491 sub SnapRestore { 
492     my $restoreto = shift;
493     my $snaptable = shift;
494     my $tableno = shift;
495     my $restoretime;
496
497     # don't do anything until connected
498     if (!defined($::client_id)) {
499         print "You must first ``connect''.\n";
500         return;
501     }
502
503     if ( ! $snaptable || ! defined $restoreto ) {
504         print "Usage: snaprestore \"restore to slot\" \"snaptable\" \"tableno\"\n";
505         return;
506     }
507
508     if ( ! -f $snaptable ) {
509         print "Table $snaptable doesn't exist\n";
510         return;
511     }
512    
513     my $table = ReadSnapShotTable($snaptable);
514     $restoretime = FindSnapInTable($table, $restoreto);
515     if ( ! defined $table->{0} || ! defined $restoretime ) {
516         PrintSnapShotTable($table);
517         print "No current or $restoreto slot in this table\n";
518         return;
519     }
520
521     my $currentindex = $table->{0};
522     if (  $table->{$restoretime} == $currentindex ) {
523         print "You should not restore to the current snapshot\n";
524         return;
525     }
526     
527     # swap the entries for 0 and $restoreto
528     my $tmp = $table->{$restoretime};
529     $table->{$restoretime} = $table->{0};
530     $table->{0} = $tmp;
531     # PrintSnapShotTable($table);
532
533     # write it back
534     WriteSnapShotTable($snaptable, $table);
535
536     # set it in the kernel
537     SnapSetTable($tableno, $snaptable);
538
539     # ready for the ioctl
540     my $err = 0;
541     my $type = "snap_obd";
542     $data = pack("i", $currentindex); # slot of previous current snapshot 
543     $datalen = 4;
544
545     my $len = length($type);
546     my $cl = length($data);
547     my $packed = pack("Lipip", $::client_id, length($type), $type, $datalen, $data);
548
549     my $rc = ioctl(DEV_OBD, &OBD_SNAP_RESTORE, $packed);
550
551     if (!defined $rc) {
552         print STDERR "ioctl failed: $!\n";
553     } elsif ($rc eq "0 but true") {
554         print "Snaprestore finished (success)\n";
555         delete $table->{$restoretime} if defined $restoretime;
556         # write it back
557         WriteSnapShotTable($snaptable, $table);
558         
559         # set it in the kernel
560         SnapSetTable($tableno, $snaptable);
561         # PrintSnapShotTable($table);
562
563     } else {
564         print "ioctl returned error code $rc.\n";
565     }
566 }
567
568 sub FindSnapInTable { 
569     my $table = shift;
570     my $snapno =shift;
571
572     foreach my $restoretime ( keys %{$table} ) {
573         if ( $table->{$restoretime} == $snapno) { 
574             print "Found key $restoretime for snapno $snapno\n";
575             return $restoretime;
576         }
577     }
578     undef;
579 }
580             
581
582 sub SnapPrint { 
583     my $err = 0;
584     my $type = "snap_obd";
585     my $snaptableno = shift;
586
587     $data = pack("i", $snaptableno);
588     $datalen = 4;
589
590     my $len = length($type);
591     my $cl = length($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         # XXX we should change to pack LL instead of I for times
633         $data .= pack("Ii", $time, $table->{$time});
634         $datalen += 8;
635     }
636
637     my $len = length($type);
638     my $cl = length($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 $src_obdo;
756     my $dst_obdo;
757
758     # Note: _copy IOCTL takes parameters as dst, src.
759     #       Copy function takes parameters as src, dst.
760     $src_obdo->{id} = shift;
761     $dst_obdo->{id} = shift;
762     $src_obdo->{valid} = &OBD_MD_FLALL;
763
764     # XXX need to fix copy so we can have 2 client IDs here
765     my $packed = pack("L", $::client_id) . obdo_pack($dst_obdo) . pack("L", $::client_id) . obdo_pack($src_obdo);
766
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
781     # Note: _migr IOCTL takes parameters as dst, src.
782     #       Migrate function takes parameters as src, dst.
783     $src_obdo->{id} = shift;
784     $dst_obdo->{id} = shift;
785     $src_obdo->{valid} = &OBD_MD_FLALL;
786
787     # We pack a dummy connection ID here
788     my $packed = pack("L", $::client_id) . obdo_pack($dst_obdo) . pack("L", $::client_id) . obdo_pack($src_obdo);
789
790     my $rc = ioctl(DEV_OBD, &OBD_IOC_MIGR, $packed);
791
792     if (!defined $rc) {
793         print STDERR "ioctl failed: $!\n";
794     } elsif ($rc eq "0 but true") {
795         print "Finished (success)\n";
796     } else {
797         print "ioctl returned error code $rc.\n";
798     }
799 }
800
801
802 sub Format {
803     my $err = 0;
804     my $size = shift;
805     my $data = pack("i", $size);
806     my $datalen = 4;
807
808     my $packed = pack("ip", $datalen, $data);
809     my $rc = ioctl(DEV_OBD, &OBD_IOC_FORMATOBD, $packed);
810
811     if (!defined $rc) {
812         print STDERR "ioctl failed: $!\n";
813     } elsif ($rc eq "0 but true") {
814         print "Finished (success)\n";
815     } else {
816         print "ioctl returned error code $rc.\n";
817     }
818 }
819
820 sub Partition {
821     my $err = 0;
822     my $partno = shift;
823     my $size = shift;
824     my $data = pack("ii", $partno, $size);
825     my $datalen = 2 * 4;
826
827     my $packed = pack("ip", $datalen, $data);
828     my $rc = ioctl(DEV_OBD, &OBD_IOC_PARTITION, $packed);
829
830     if (!defined $rc) {
831         print STDERR "ioctl failed: $!\n";
832     } elsif ($rc eq "0 but true") {
833         print "Finished (success)\n";
834     } else {
835         print "ioctl returned error code $rc.\n";
836     }
837 }
838
839 sub Setup {
840     my $err = 0;
841     my $arg = shift;
842     my $data;
843     my $datalen = 0;
844
845     # XXX we need a getinfo ioctl to validate parameters 
846     # by type here
847
848     if ($arg && !defined($::st = stat($arg))) {
849             print "$arg is not a valid device\n";
850             return;
851     }
852     
853     if ( $arg ) {
854         $dev = $::st->rdev() unless $dev;
855         $data = pack("i", $dev);
856         $datalen = 4;
857     }
858
859     my $packed = pack("ip", $datalen, $data);
860     my $rc = ioctl(DEV_OBD, &OBD_IOC_SETUP, $packed);
861
862     if (!defined $rc) {
863         print STDERR "ioctl failed: $!\n";
864     } elsif ($rc eq "0 but true") {
865         print "Finished (success)\n";
866     } else {
867         print "ioctl returned error code $rc.\n";
868     }
869 }
870
871 sub Cleanup {
872     my $err = "0";
873     my $rc = ioctl(DEV_OBD, &OBD_IOC_CLEANUP, $err);
874
875     if (!defined $rc) {
876         print STDERR "ioctl failed: $!\n";
877     } elsif ($rc eq "0 but true") {
878         print "Finished (success)\n";
879         $::client_id = 0;
880     } else {
881         print "ioctl returned error code $rc.\n";
882     }
883 }
884
885
886 sub Connect {
887     my $rc;
888
889     my $packed = "";
890     $rc = ioctl(DEV_OBD, &OBD_IOC_CONNECT, $packed);
891     $id = unpack("I", $packed);
892
893     if (!defined $rc) {
894         print STDERR "ioctl failed: $!\n";
895     } elsif ($rc eq "0 but true") {
896         $::client_id = $id;
897         print "Client ID     : $id\n";
898         print "Finished (success)\n";
899     } else {
900         print "ioctl returned error code $rc.\n";
901     }
902 }
903
904 sub Disconnect {
905     my $id = shift;
906
907     if (!defined($id)) {
908         $id = $::client_id;
909     }
910
911     if (!defined($id)) {
912         print "syntax: disconnect [client ID]\n";
913         print "When client ID is not given, the last valid client ID to be returned by a\n";
914         print "connect command this session is used; there is no such ID.\n";
915         return;
916     }
917
918     my $packed = pack("L", $id);
919     my $rc = ioctl(DEV_OBD, &OBD_IOC_DISCONNECT, $packed);
920
921     if (!defined $rc) {
922         print STDERR "ioctl failed: $!\n";
923     } elsif ($rc eq "0 but true") {
924         $::client_id = undef;
925         print "Finished (success)\n";
926     } else {
927         print "ioctl returned error code $rc.\n";
928     }
929 }
930
931 sub Create {
932     my $arg = shift;
933     my $quiet = shift;
934     my $rc;
935     my $prealloc = 0;
936
937     if (defined($quiet) && $quiet ne "quiet") {
938         print "syntax: create [number of objects [quiet]]\n";
939         return;
940     }
941
942     my $packed = pack("IL", $::client_id, $prealloc);
943     if (!defined($arg) || scalar($arg) < 2) {
944         print "Creating 1 object...\n";
945         $rc = ioctl(DEV_OBD, &OBD_IOC_CREATE, $packed);
946         if (!defined($quiet)) {
947             my $obdo = obdo_unpack($packed, 4);
948             print "Created object #$obdo->{id}.\n";
949         }
950     } else {
951         my $i;
952
953         print "Creating " . scalar($arg) . " objects...\n";
954         for ($i = 0; $i < scalar($arg); $i++) {
955             $rc = ioctl(DEV_OBD, &OBD_IOC_CREATE, $packed);
956             my $ino = unpack("L", $packed);
957             if ($rc ne "0 but true") {
958                 last;
959                 $packed = pack("IL", $::client_id, $prealloc);
960             } elsif (!defined($quiet)) {
961                 $packed = pack("IL", $::client_id, $prealloc);
962                 print "Created object #$ino.\n";
963             }
964         }
965     }
966
967     if (!defined $rc) {
968         print STDERR "ioctl failed: $!\n";
969     } elsif ($rc eq "0 but true") {
970         print "Finished (success)\n";
971     } else {
972         print "ioctl returned error code $rc.\n";
973     }
974 }
975
976 sub Sync {
977     my $err = "0";
978     my $rc = ioctl(DEV_OBD, &OBD_IOC_SYNC, $err);
979
980     if (!defined $rc) {
981         print STDERR "ioctl failed: $!\n";
982     } elsif ($rc eq "0 but true") {
983         print "Finished (success)\n";
984     } else {
985         print "ioctl returned error code $rc.\n";
986     }
987 }
988
989 sub Destroy {
990     if (!defined($::client_id)) {
991         print "You must first ``connect''.\n";
992         return;
993     }
994
995     my $arg = shift;
996
997     if (!defined($arg) || scalar($arg) < 1) {
998         print "usage: destroy <object number>\n";
999         return;
1000     }
1001
1002     print "Destroying object $arg...\n";
1003     my $packed = pack("IL", $::client_id, $arg);
1004     my $rc = ioctl(DEV_OBD, &OBD_IOC_DESTROY, $packed);
1005
1006     if (!defined $rc) {
1007         print STDERR "ioctl failed: $!\n";
1008     } elsif ($rc eq "0 but true") {
1009         print "Finished (success)\n";
1010     } else {
1011         print "ioctl returned error code $rc.\n";
1012     }
1013 }
1014
1015 sub Getattr {
1016     if (!defined($::client_id)) {
1017         print "You must first ``connect''.\n";
1018         return;
1019     }
1020
1021     my $id = shift;
1022
1023     if (!defined($id) || scalar($id) < 1) {
1024         print "invalid arguments; type \"help getattr\" for a synopsis\n";
1025         return;
1026     }
1027
1028     # see Setattr
1029     my $obdo;
1030     $obdo->{id} = $id;
1031     $obdo->{valid} = &OBD_MD_FLALL;
1032     my $packed = pack("L", $::client_id) . obdo_pack($obdo);
1033     my $rc = ioctl(DEV_OBD, &OBD_IOC_GETATTR, $packed);
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 $id = shift;
1052
1053     if (!defined($id) || scalar($id) < 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} = $id;
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 }