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