Whamcloud - gitweb
d070c038d3d459dc5c6fc3a55f41d5a908c922e9
[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 [<num> [<mode> [quiet]]]: create new object(s) (files, unless mode is given)"},
213      'attach' => {func => "Attach", doc => "attach { obdext2 | obdsnap snapdev snapidx tableno | obdscsi adapter bus tid lun }: attach this minor device to the specified driver" },
214      'detach' => {func => "Detach", doc => "detach this minor 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      'partition' => {func => "Partition", doc => "partition <type> <adapter> <bus> <tid> <lun> <partition> <size>: create a partition"},
224      'format' => {func => "Format", doc => "format <type> <adapter> <bus> <tid> <lun> <size>: format a partition"},
225      'setup' => {func => "Setup", doc => "setup [type]: link this OBD device to the underlying device (default type obdext2)"},
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 <id>: destroys an object"},
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 <id> <count> [offset]: read data from object"},
233      'fsread' => {func => "Read2", doc => "read <id> <count> [offset]: read data from object"},
234      'write' => {func => "Write", doc => "write <id> <offset> <text>: write data to object"},
235      'setattr' => {func => "Setattr", doc => "setattr <id> [mode [uid [gid [size [atime [mtime [ctime]]]]]]]: sets object attributes"},
236      'getattr' => {func => "Getattr", doc => "getattr <id>: displays object attributes"},
237      'preallocate' => {func => "Preallocate", doc => "preallocate [num]: requests preallocation of num objects."},
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 @cmdline = split(' ', $line);
321     my $word = shift @cmdline;
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}}(@cmdline));
336     }
337
338     # Call the function.
339     return (&{$commands{$cmd}->{func}}(@cmdline));
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 {obdext2 | obdsnap | obdscsi}\n";
371         return;
372     }
373
374     if ($type eq "obdscsi" ) {
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 "obdsnap" ) {
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 "obdext2") {
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 = "obdext2";
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 = "obdsnap";
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 = "obdsnap";
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 = "obdsnap";
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 = "obdsnap";
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 "$dev 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     if (!defined($::client_id)) {
933         print "You must first ``connect''.\n";
934         return;
935     }
936
937     my $num = shift;
938     my $mode = shift;
939     my $quiet = shift;
940     my $rc;
941     my $prealloc = 0;
942
943     if (!defined($num)) {
944         $num = 1;
945     }
946
947     if (!defined($mode)) {
948         $mode = 0100644;         # create a file (rw-r--r--) if not specified
949     }
950
951     if (scalar($num) < 1 || defined($quiet) && $quiet ne "quiet") {
952         print "usage: create [<number of objects> [<mode> [quiet]]]\n";
953         return;
954     }
955
956     my $i;
957     my $id = 0;                 # can't currently request IDs
958
959     print "Creating " . scalar($num) . " object";
960     if (scalar($num) > 1) {
961         print "s";
962     }
963     print "\n";
964
965     for ($i = 0; $i < scalar($num); $i++) {
966         my $obdo;
967         $obdo->{id} = $id;
968         $obdo->{mode} = scalar($mode);
969         $obdo->{valid} = &OBD_MD_FLMODE;
970
971         my $packed = pack("I", $::client_id) . obdo_pack($obdo);
972         $rc = ioctl(DEV_OBD, &OBD_IOC_CREATE, $packed);
973         if ($rc ne "0 but true") {
974             last;
975         } elsif (!defined($quiet)) {
976             $obdo = obdo_unpack($packed, 4);
977             print "Created object #$obdo->{id}.\n";
978         }
979     }
980
981     if (!defined $rc) {
982         print STDERR "ioctl failed: $!\n";
983     } elsif ($rc eq "0 but true") {
984         print "Finished (success)\n";
985     } else {
986         print "ioctl returned error code $rc.\n";
987     }
988 }
989
990 sub Sync {
991     my $err = "0";
992     my $rc = ioctl(DEV_OBD, &OBD_IOC_SYNC, $err);
993
994     if (!defined $rc) {
995         print STDERR "ioctl failed: $!\n";
996     } elsif ($rc eq "0 but true") {
997         print "Finished (success)\n";
998     } else {
999         print "ioctl returned error code $rc.\n";
1000     }
1001 }
1002
1003 sub Destroy {
1004     if (!defined($::client_id)) {
1005         print "You must first ``connect''.\n";
1006         return;
1007     }
1008
1009     my $id = shift;
1010
1011     if (!defined($id) || scalar($id) < 1) {
1012         print "usage: destroy <object number>\n";
1013         return;
1014     }
1015
1016     print "Destroying object $id...\n";
1017     my $packed = pack("IL", $::client_id, $id);
1018     my $rc = ioctl(DEV_OBD, &OBD_IOC_DESTROY, $packed);
1019
1020     if (!defined $rc) {
1021         print STDERR "ioctl failed: $!\n";
1022     } elsif ($rc eq "0 but true") {
1023         print "Finished (success)\n";
1024     } else {
1025         print "ioctl returned error code $rc.\n";
1026     }
1027 }
1028
1029 sub Getattr {
1030     if (!defined($::client_id)) {
1031         print "You must first ``connect''.\n";
1032         return;
1033     }
1034
1035     my $id = shift;
1036
1037     if (!defined($id) || scalar($id) < 1) {
1038         print "invalid arguments; type \"help getattr\" for a synopsis\n";
1039         return;
1040     }
1041
1042     # see Setattr
1043     my $obdo;
1044     $obdo->{id} = $id;
1045     $obdo->{valid} = &OBD_MD_FLALL;
1046     my $packed = pack("L", $::client_id) . obdo_pack($obdo);
1047     my $rc = ioctl(DEV_OBD, &OBD_IOC_GETATTR, $packed);
1048     
1049     if (!defined $rc) {
1050         print STDERR "ioctl failed: $!\n";
1051     } elsif ($rc eq "0 but true") {
1052         $obdo = obdo_unpack($packed,  4); 
1053         obdo_print($obdo);
1054     } else {
1055         print "ioctl returned error code $rc.\n";
1056     }
1057 }
1058
1059 sub Setattr {
1060     if (!defined($::client_id)) {
1061         print "You must first ``connect''.\n";
1062         return;
1063     }
1064
1065     my $id = shift;
1066
1067     if (!defined($id) || scalar($id) < 1) {
1068         print "invalid arguments; type \"help setattr\" for a synopsis\n";
1069         return;
1070     }
1071
1072     # XXX we do not currently set all of the fields in the obdo
1073     my $obdo;
1074     $obdo->{id} = $id;
1075     $obdo->{mode} = oct(shift);
1076     $obdo->{uid} = shift;
1077     $obdo->{gid} = shift;
1078     $obdo->{size} = shift;
1079     $obdo->{atime} = shift;
1080     $obdo->{mtime} = shift;
1081     $obdo->{ctime} = shift;
1082     $obdo->{valid} = 0;
1083
1084     if (defined($obdo->{atime})) {
1085         $obdo->{valid} |= &OBD_MD_FLATIME;
1086     }
1087     if (defined($obdo->{mtime})) {
1088         $obdo->{valid} |= &OBD_MD_FLMTIME;
1089     }
1090     if (defined($obdo->{ctime})) {
1091         $obdo->{valid} |= &OBD_MD_FLCTIME;
1092     }
1093     if (defined($obdo->{size})) {
1094         $obdo->{valid} |= &OBD_MD_FLSIZE;
1095     }
1096     if (defined($obdo->{mode})) {
1097         $obdo->{valid} |= &OBD_MD_FLMODE;
1098     }
1099     if (defined($obdo->{uid})) {
1100         $obdo->{valid} |= &OBD_MD_FLUID;
1101     }
1102     if (defined($obdo->{gid})) {
1103         $obdo->{valid} |= &OBD_MD_FLGID;
1104     }
1105
1106     printf "valid is %x, mode is %o\n", $obdo->{valid}, $obdo->{mode};
1107     my $packed = pack("L", $::client_id) . obdo_pack($obdo);
1108     my $rc = ioctl(DEV_OBD, &OBD_IOC_SETATTR, $packed);
1109
1110     if (!defined $rc) {
1111         print STDERR "ioctl failed: $!\n";
1112     } elsif ($rc eq "0 but true") {
1113         print "Finished (success)\n";
1114     } else {
1115         print "ioctl returned error code $rc.\n";
1116     }
1117 }
1118
1119 sub Read {
1120     if (!defined($::client_id)) {
1121         print "You must first ``connect''.\n";
1122         return;
1123     }
1124
1125     my $id = shift;
1126     my $count = shift;
1127     my $offset = shift;
1128   
1129     if (!defined($id) || scalar($id) < 1 || !defined($count) ||
1130         $count < 1 || (defined($offset) && $offset < 0)) {
1131         print "invalid arguments; type \"help read\" for a synopsis\n";
1132         return;
1133     }
1134
1135     if (!defined($offset)) {
1136         $offset = 0;
1137     }
1138
1139     print("Reading $count bytes starting at byte $offset from object " .
1140           "$id...\n");
1141
1142     # "allocate" a large enough buffer
1143     my $buf = sprintf("%${count}s", " ");
1144     die "suck" if (length($buf) != $count);
1145
1146     my $obdo;
1147     $obdo->{id} = $id;
1148
1149     # the perl we're using doesn't support pack type Q, and offset is 64 bits
1150     my $packed = pack("L", $::client_id) . obdo_pack($obdo) .
1151                  pack("p LL LL", $buf, $count, $offset);
1152
1153     my $rc = ioctl(DEV_OBD, &OBD_IOC_READ, $packed);
1154
1155     $retval = unpack("l", $packed);
1156
1157     if (!defined $rc) {
1158         print STDERR "ioctl failed: $!\n";
1159     } elsif ($rc eq "0 but true") {
1160         if ($retval >= 0) {
1161                 print substr($buf, 0, $retval);
1162                 print "\nRead $retval of an attempted $count bytes.\n";
1163                 print "Finished (success)\n";
1164         } else {
1165                 print "Finished (error $retval)\n";
1166         }
1167     } else {
1168         print "ioctl returned error code $rc.\n";
1169     }
1170 }
1171
1172 sub Read2 {
1173     if (!defined($::client_id)) {
1174         print "You must first ``connect''.\n";
1175         return;
1176     }
1177
1178     my $id = shift;
1179     my $count = shift;
1180     my $offset = shift;
1181   
1182     if (!defined($id) || scalar($id) < 1 || !defined($count) ||
1183         $count < 1 || (defined($offset) && $offset < 0)) {
1184         print "invalid arguments; type \"help read\" for a synopsis\n";
1185         return;
1186     }
1187
1188     if (!defined($offset)) {
1189         $offset = 0;
1190     }
1191
1192     print("Reading $count bytes starting at byte $offset from object " .
1193           "$id...\n");
1194
1195     # "allocate" a large enough buffer
1196     my $buf = sprintf("%${count}s", " ");
1197     die "suck" if (length($buf) != $count);
1198
1199     my $obdo;
1200     $obdo->{id} = $id;
1201
1202     # the perl we're using doesn't support pack type Q, and offset is 64 bits
1203     my $packed = pack("L", $::client_id) . obdo_pack($obdo) .
1204                  pack("p LL LL", $buf, $count, $offset);
1205
1206     my $rc = ioctl(DEV_OBD, &OBD_IOC_READ2, $packed);
1207
1208     $retval = unpack("l", $packed);
1209
1210     if (!defined $rc) {
1211         print STDERR "ioctl failed: $!\n";
1212     } elsif ($rc eq "0 but true") {
1213         if ($retval >= 0) {
1214                 print substr($buf, 0, $retval);
1215                 print "\nRead $retval of an attempted $count bytes.\n";
1216                 print "Finished (success)\n";
1217         } else {
1218                 print "Finished (error $retval)\n";
1219         }
1220     } else {
1221         print "ioctl returned error code $rc.\n";
1222     }
1223 }
1224
1225 sub Write {
1226     if (!defined($::client_id)) {
1227         print "You must first ``connect''.\n";
1228         return;
1229     }
1230
1231     my $id = shift;
1232     my $offset = shift;
1233     my $text = join(' ', @_);
1234     my $count = length($text);
1235
1236     if (!defined($id) || scalar($id) < 1 || !defined($offset) ||
1237         scalar($offset) < 0) {
1238         print "invalid arguments; type \"help write\" for a synopsis\n";
1239         return;
1240     }
1241
1242     if (!defined($text)) {
1243         $text = "";
1244         $count = 0;
1245     }
1246
1247     print("Writing $count bytes starting at byte $offset to object " .
1248           "$id...\n");
1249
1250     my $obdo;
1251     $obdo->{id} = $id;
1252
1253     # the perl we're using doesn't support pack type Q
1254     my $packed = pack("L", $::client_id) . obdo_pack($obdo) .
1255                  pack("p LL LL", $buf, $count, $offset);
1256
1257     my $rc = ioctl(DEV_OBD, &OBD_IOC_WRITE, $packed);
1258
1259     $retval = unpack("l", $packed);
1260
1261     if (!defined $rc) {
1262         print STDERR "ioctl failed: $!\n";
1263     } elsif ($rc eq "0 but true") {
1264         if ($retval >= 0) {
1265                 print "\nWrote $retval of an attempted $count bytes.\n";
1266                 print "Finished (success)\n";
1267         } else {
1268                 print "Finished (error $retval)\n";
1269         }
1270     } else {
1271         print "ioctl returned error code $rc.\n";
1272     }
1273 }
1274
1275 sub Preallocate {
1276     my $num = shift;
1277
1278     if (!defined($::client_id)) {
1279         print "You must first ``connect''.\n";
1280         return;
1281     }
1282
1283     if (!defined($num) || scalar($num) < 1 || scalar($num) > 32) {
1284         $num = 32;
1285     }
1286
1287     print "Preallocating $num objects...\n";
1288     # client id, alloc, id[32]
1289     my $packed = pack("LLx128", $::client_id, $num);
1290
1291     my $rc = ioctl(DEV_OBD, &OBD_IOC_PREALLOCATE, $packed);
1292
1293     if (!defined $rc) {
1294         print STDERR "ioctl failed: $!\n";
1295     } elsif ($rc eq "0 but true") {
1296         my $alloc = unpack("x4L", $packed);
1297         my @ids = unpack("x8L32", $packed);
1298         my $i;
1299
1300         print "Got $alloc objects: ";
1301         foreach $i (@ids) {
1302             print $i . " ";
1303         }
1304         print "\nFinished (success)\n";
1305     } else {
1306         print "ioctl returned error code $rc.\n";
1307     }
1308 }
1309
1310 sub Decusecount {
1311     my $rc = ioctl(DEV_OBD, &OBD_IOC_DEC_USE_COUNT, 0);
1312
1313     if (!defined $rc) {
1314         print STDERR "ioctl failed: $!\n";
1315     } elsif ($rc eq "0 but true") {
1316         print "Finished (success)\n";
1317     } else {
1318         print "ioctl returned error code $rc.\n";
1319     }
1320 }
1321
1322 sub Statfs {
1323     if (!defined($::client_id)) {
1324         print "You must first ``connect''.\n";
1325         return;
1326     }
1327
1328     # struct statfs {
1329     #         long f_type;
1330     #         long f_bsize;
1331     #         long f_blocks;
1332     #         long f_bfree;
1333     #         long f_bavail;
1334     #         long f_files;
1335     #         long f_ffree;
1336     #         __kernel_fsid_t f_fsid; (64 bits)
1337     #         long f_namelen;
1338     #         long f_spare[6];
1339     # };
1340
1341     my $packed = pack("LLLLLLLIILL6", $::client_id, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1342                       0, 0, 0, 0, 0, 0);
1343
1344     my $rc = ioctl(DEV_OBD, &OBD_IOC_STATFS, $packed);
1345
1346     if (!defined $rc) {
1347         print STDERR "ioctl failed: $!\n";
1348     } elsif ($rc eq "0 but true") {
1349         # skip both the conn_id and the fs_type in the buffer
1350         my ($bsize, $blocks, $bfree, $bavail, $files, $ffree) =
1351             unpack("x4x4LLLLLL", $packed);
1352         print("$bsize byte blocks: $blocks, " . ($blocks - $bfree) . " used, " .
1353               "$bfree free ($bavail available).\n");
1354         print "$files files, " . ($files - $ffree) . " used, $ffree free.\n";
1355         print "Finished (success)\n";
1356     } else {
1357         print "ioctl returned error code $rc.\n";
1358     }
1359 }
1360
1361 sub Help {
1362     my $cmd = shift;
1363
1364     if ( !$cmd || !$commands{$cmd} ) {
1365         print "Comands: ", join( ' ', @jcm_cmd_list), "\n";
1366     } else {
1367         print "Usage: " .  $commands{$cmd}->{doc} . "\n";
1368     }
1369 }
1370
1371 sub Quit {
1372     if ($::client_id) {
1373         print "Disconnecting active session ($::client_id)...";
1374         Disconnect($::client_id);
1375     }
1376     exit;
1377 }