Whamcloud - gitweb
Two small changes to support large files.
[fs/lustre-release.git] / lustre / utils / 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 # FIXME: obsolete?
33 eval 'sub OBD_IOC_DEC_USE_COUNT () { &_IOC(0, ord(\'f\'), 8, 0);}' unless
34   defined(&OBD_IOC_DEC_USE_COUNT);
35 eval 'sub OBD_IOC_SETATTR () { &_IOC(1, ord(\'f\'), 9, 4);}' unless
36   defined(&OBD_IOC_SETATTR);
37 eval 'sub OBD_IOC_GETATTR () { &_IOC(2, ord(\'f\'), 10, 4);}' unless
38   defined(&OBD_IOC_GETATTR);
39 eval 'sub OBD_IOC_READ () { &_IOC(3, ord(\'f\'), 11, 4);}' unless
40   defined(&OBD_IOC_READ);
41 eval 'sub OBD_IOC_WRITE () { &_IOC(3, ord(\'f\'), 12, 4);}' unless
42   defined(&OBD_IOC_WRITE);
43 eval 'sub OBD_IOC_CONNECT () { &_IOC(2, ord(\'f\'), 13, 4);}' unless
44   defined(&OBD_IOC_CONNECT);
45 eval 'sub OBD_IOC_DISCONNECT () { &_IOC(1, ord(\'f\'), 14, 4);}' unless
46   defined(&OBD_IOC_DISCONNECT);
47 eval 'sub OBD_IOC_STATFS () { &_IOC(3, ord(\'f\'), 15, 4);}' unless
48   defined(&OBD_IOC_STATFS);
49 eval 'sub OBD_IOC_SYNC () { &_IOC(2, ord(\'f\'), 16, 4);}' unless
50   defined(&OBD_IOC_SYNC);
51 # FIXME: obsolete?
52 eval 'sub OBD_IOC_READ2 () { &_IOC(3, ord(\'f\'), 17, 4);}' unless
53   defined(&OBD_IOC_READ2);
54 # FIXME: obsolete?
55 eval 'sub OBD_IOC_FORMATOBD () { &_IOC(3, ord(\'f\'), 18, 4);}' unless
56   defined(&OBD_IOC_FORMATOBD);
57 # FIXME: obsolete?
58 eval 'sub OBD_IOC_PARTITION () { &_IOC(3, ord(\'f\'), 19, 4);}' unless
59   defined(&OBD_IOC_PARTITION);
60 eval 'sub OBD_IOC_ATTACH () { &_IOC(3, ord(\'f\'), 20, 4);}' unless
61   defined(&OBD_IOC_ATTACH);
62 eval 'sub OBD_IOC_DETACH () { &_IOC(3, ord(\'f\'), 21, 4);}' unless
63   defined(&OBD_IOC_DETACH);
64 eval 'sub OBD_IOC_COPY () { &_IOC(3, ord(\'f\'), 22, 4);}' unless
65   defined(&OBD_IOC_COPY);
66 eval 'sub OBD_IOC_MIGR () { &_IOC(3, ord(\'f\'), 23, 4);}' unless
67   defined(&OBD_IOC_MIGR);
68 eval 'sub OBD_IOC_PUNCH () { &_IOC(3, ord(\'f\'), 24, 4);}' unless
69   defined(&OBD_IOC_PUNCH);
70 eval 'sub OBD_SNAP_SETTABLE () { &_IOC(3, ord(\'f\'), 40, 4);}' unless
71   defined(&OBD_SNAP_SETTABLE);
72 eval 'sub OBD_SNAP_PRINTTABLE () { &_IOC(3, ord(\'f\'), 41, 4);}' unless
73   defined(&OBD_SNAP_PRINTTABLE);
74 eval 'sub OBD_SNAP_DELETE() { &_IOC(3, ord(\'f\'), 42, 4);}' unless
75   defined(&OBD_SNAP_DELETE);
76 eval 'sub OBD_SNAP_RESTORE() { &_IOC(3, ord(\'f\'), 43, 4);}' unless
77   defined(&OBD_SNAP_RESTORE);
78
79 eval 'sub OBD_EXT2_RUNIT () { &_IOC(3, ord(\'f\'), 61, 4);}' unless
80   defined(&OBD_EXT2_RUNIT);
81
82 eval 'sub OBD_MD_FLALL   () {~0;}'   unless defined(&OBD_MD_FLALL);
83 eval 'sub OBD_MD_FLATIME () {1<<1;}' unless defined(&OBD_MD_FLATIME);
84 eval 'sub OBD_MD_FLMTIME () {1<<2;}' unless defined(&OBD_MD_FLMTIME);
85 eval 'sub OBD_MD_FLCTIME () {1<<3;}' unless defined(&OBD_MD_FLCTIME);
86 eval 'sub OBD_MD_FLSIZE  () {1<<4;}' unless defined(&OBD_MD_FLSIZE);
87 eval 'sub OBD_MD_FLMODE  () {1<<7;}' unless defined(&OBD_MD_FLMODE);
88 eval 'sub OBD_MD_FLUID   () {1<<8;}' unless defined(&OBD_MD_FLUID);
89 eval 'sub OBD_MD_FLGID   () {1<<9;}' unless defined(&OBD_MD_FLGID);
90
91 use Getopt::Long;
92 use File::stat;
93 use Storable;
94 use Carp;
95 use Term::ReadLine;
96 use IO::Handle;
97 use Pack;
98
99
100 # NOTE long long are layed out in ia32 memory as follows:
101 # u = 0xaaaabbbbccccdddd has ccccdddd at &u and aaaabbbb 4 bytes on
102 # this may be different on other architectures
103
104 # we use 32-bit integers for all 64-bit quantities in this program
105 # #define OBD_INLINESZ  60
106 # #define OBD_OBDMDSZ   60
107 # /* Note: 64-bit types are 64-bit aligned in structure */
108 # struct obdo {
109 #       obd_id                  o_id;
110 #       obd_gr                  o_gr;
111 #       obd_time                o_atime;
112 #       obd_time                o_mtime;
113 #       obd_time                o_ctime;
114 #       obd_size                o_size;
115 #       obd_blocks              o_blocks;
116 #       obd_blksize             o_blksize;
117 #       obd_mode                o_mode;
118 #       obd_uid                 o_uid;
119 #       obd_gid                 o_gid;
120 #       obd_flag                o_flags;
121 #       obd_flag                o_obdflags;
122 #       obd_count               o_nlink;
123 #       obd_count               o_generation;
124 #       obd_flag                o_valid;        /* hot fields in this obdo */
125 #       char                    o_inline[60];
126 #       char                    o_obdmd[60];
127 #       struct list_head        o_list;
128 #       struct obd_ops          *o_op;
129 # };
130
131 sub obdo_pack {
132     my $obdo = shift;
133     pack "LL LL LL LL LL LL LL L L L L L L L L L a60 a60 L L L", 
134     $obdo->{id}, 0, 
135     $obdo->{gr}, 0, 
136     $obdo->{atime}, 0, 
137     $obdo->{mtime}, 0 ,
138     $obdo->{ctime}, 0, 
139     $obdo->{size}, 0, 
140     $obdo->{blocks}, 0, 
141     $obdo->{blksize},
142     $obdo->{mode},
143     $obdo->{uid},
144     $obdo->{gid},
145     $obdo->{flags},
146     $obdo->{obdflags},
147     $obdo->{nlink},     
148     $obdo->{generation},        
149     $obdo->{valid},     
150     $obdo->{inline},
151     $obdo->{obdmd},
152     0, 0, # struct list_head 
153     0;  #  struct obd_ops 
154 }
155
156 sub obdo_unpack {
157     my $buf = shift;
158     my $offset = shift;
159     my $obdo;
160     ($obdo->{id},
161     $obdo->{gr},
162     $obdo->{atime},
163     $obdo->{mtime},
164     $obdo->{ctime},
165     $obdo->{size},
166     $obdo->{blocks},
167     $obdo->{blksize},
168     $obdo->{mode},
169     $obdo->{uid},
170     $obdo->{gid},
171     $obdo->{flags},
172     $obdo->{obdflags},
173     $obdo->{nlink},
174     $obdo->{generation},
175     $obdo->{valid},
176     $obdo->{inline},
177     $obdo->{obdmd}) = unpack "x${offset}Lx4 Lx4 Lx4 Lx4 Lx4 Lx4 Lx4 L L L L L L L L L a60 a60", $buf;
178     $obdo;
179 }
180
181 sub obdo_print {
182
183     my $obdo = shift;
184
185     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",
186     $obdo->{id},
187     $obdo->{gr},
188     $obdo->{atime},
189     $obdo->{mtime},
190     $obdo->{ctime},
191     $obdo->{size},
192     $obdo->{blocks},
193     $obdo->{blksize},
194     $obdo->{mode},
195     $obdo->{uid},
196     $obdo->{gid},
197     $obdo->{flags},
198     $obdo->{obdflags},
199     $obdo->{nlink},
200     $obdo->{valid},
201     $obdo->{inline},
202     $obdo->{obdmd};
203 }
204
205
206 my ($file);
207
208 GetOptions("f!" => \$file, "device=s" => \$::device, ) || die "Getoptions";
209
210
211 # get a console for the app
212
213 my $line;
214 my $command;
215 my $arg;
216
217 my @procsysobd_objects = ('debug', 'index', 'reset', 'trace', 'vars');
218
219 my %commands =
220     ('status' => {func => "Status", doc => "status: show obd device status"},
221      'procsys' => {func => "Procsys", doc => "procsys <file> <value> (set /proc/sys/obd configuration)"},
222      'shell' => {func => "Shell", doc => "shell <shell-command>: execute shell-commands"},
223      'script' => {func => "Script", doc => "script <filename>: read and execute commands from a file"},
224      'insmod' => {func => "Insmod", doc => "insmod <module>: insert kernel module"},
225      'rmmod' => {func => "Rmmod", doc => "rmmod <module>: insert kernel module"},
226      'lsmod' => {func => "Lsmod", doc => "lsmod <module>: list kernel modules"},
227      'device' => {func => "Device", doc => "device <dev>: open another OBD device"},
228      'close' => {func => "Close", doc => "close <dev>: close OBD device"},
229      'create' => {func => "Create", doc => "create [<num> [<mode> [quiet]]]: create new object(s) (files, unless mode is given)"},
230      'attach' => {func => "Attach", doc => "attach { obdext2 | obdsnap snapdev snapidx tableno | obdscsi adapter bus tid lun }: attach this minor device to the specified driver" },
231      'detach' => {func => "Detach", doc => "detach this minor device"},
232      'testext2iterator' => {func => "TestExt2Iterator", doc => "test ext2 iterator function"},
233      'snapset' => {func => "SnapSetTable", doc => "snapset <tableno> <file>: set the table (created with snaptable) as table #tableno" },
234      'snapprint' => {func => "SnapPrint", doc => "snapprint <tableno>: output the contents of table #tableno to the syslog"},
235      'snapdelete' => {func => "SnapDelete", doc => "snapdelete: delete connected snap obd objects from disk"},
236      'snaprestore' => {func => "SnapRestore", doc => "snaprestore : restore connected old snap objects to be current"},
237      'snaptable' => {func => "SnapShotTable", doc => "snaptable: build a snapshot table (interactive)"},
238      'copy' => {func => "Copy", doc => "copy <srcid> <tgtid>: copy objects"},
239      'migrate' => {func => "Migrate", doc => "migrate <srcid> <tgtid>: migrate data from one object to another"},
240 # FIXME: obsolete?
241      'partition' => {func => "Partition", doc => "partition <type> <adapter> <bus> <tid> <lun> <partition> <size>: create a partition"},
242 # FIXME: obsolete?
243      'format' => {func => "Format", doc => "format <type> <adapter> <bus> <tid> <lun> <size>: format a partition"},
244      'setup' => {func => "Setup", doc => "setup [type]: link this OBD device to the underlying device (default type obdext2)"},
245      'connect' => {func => "Connect", doc => "connect: allocates client ID for this session"},
246      'disconnect' => {func => "Disconnect", doc => "disconnect [id]: frees client resources"},
247      'sync' => {func => "Sync", doc => "sync: flushes buffers to disk"},
248      'destroy' => {func => "Destroy", doc => "destroy <id>: destroys an object"},
249      'cleanup' => {func => "Cleanup", doc => "cleanup the minor obd device"},
250 # FIXME: obsolete?
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 <id> <count> [offset]: read data from object"},
253 # FIXME: obsolete?
254      'fsread' => {func => "Read2", doc => "read <id> <count> [offset]: read data from object"},
255      'write' => {func => "Write", doc => "write <id> <offset> <text>: write data to object"},
256      'punch' => {func => "Punch", doc => "punch <id> <start> <count>: punch a hole in object"},
257      'setattr' => {func => "Setattr", doc => "setattr <id> [mode [uid [gid [size [atime [mtime [ctime]]]]]]]: sets object attributes"},
258      'getattr' => {func => "Getattr", doc => "getattr <id>: displays object attributes"},
259      'preallocate' => {func => "Preallocate", doc => "preallocate [num]: requests preallocation of num objects."},
260      'statfs' => {func => "Statfs", doc => "statfs: filesystem status information"},
261      'help' => {func => \&Help,  doc => "help: this message"},
262      'quit' => {func => \&Quit,  doc => "see \"exit\""},
263      'exit' => {func => \&Quit,  doc => "see \"quit\""}
264     );
265
266 #
267 #       setup completion function
268 #
269 my @jcm_cmd_list = keys %commands;
270
271 my $term, $attribs;
272
273
274 # Get going....
275
276 Device($::device);
277
278 sub readl {
279     if ( $file ) {
280         my $str = <STDIN>;
281         chop($str);
282         return $str;
283     } else {
284         return $term->readline(@_);
285     }
286 }
287
288
289
290 if ( $file ) {
291     while ( <STDIN> ) {
292         print $_;
293         my $rc = execute_line($_);
294         if ($rc != 0) { last; }
295     }
296     exit 0;
297 } else {
298     $term = new Term::ReadLine 'obdcontrol ';
299     $attribs = $term->Attribs;
300     $attribs->{attempted_completion_function} = \&completeme;
301     $term->ornaments('md,me,,');        # bold face prompt
302     
303     # make sure stdout is not buffered
304     STDOUT->autoflush(1);
305
306
307     # Get on with the show
308     process_line();
309 }
310
311 #------------------------------------------------------------------------------
312 sub completeme {
313     my ($text, $line, $start, $end) = @_;
314     if (substr($line, 0, $start) =~ /^\s*$/) {
315         if ($] < 5.6) { # PErl version is less than 5.6.0
316             return (exists $commands{$text}) ? $text : 0;
317 #Above line doesn't perform command completion, but
318 #perl5.005 Term-ReadLine lacks support for completion matching
319 #and perl5.6.0 requires glibc2.2.2 that won't run under Redhat6.2......sigh.
320         }
321         else {
322             $attribs->{completion_word} = \@jcm_cmd_list;
323             return $term->completion_matches($text,
324                        $attribs->{'list_completion_function'});
325         }
326     }
327 }
328
329 sub find_command {
330     my $given = shift;
331     my $name;
332     my @completions = completeme($given, $given, 0, length($given));
333     if ($#completions == 0) {
334         $name = shift @completions;
335     }
336
337     return $name;
338 }
339
340 # start making requests
341 sub process_line {
342   foo:
343     $line = $term->readline("obdcontrol > ");
344     execute_line($line);
345     goto foo;
346 }
347
348 sub execute_line {
349     my $line = shift;
350
351     my @cmdline = split(' ', $line);
352     my $word = shift @cmdline;
353
354     return 0 unless ($word);
355
356     my $cmd;
357     if ( $file ) {
358         $cmd = $word;
359     } else {
360         $cmd = find_command($word);
361     }
362     unless ($cmd) {
363         printf STDERR "$word: No such command, or not unique.\n";
364         return (-1);
365     }
366
367     # Call the function.
368     return (&{$commands{$cmd}->{func}}(@cmdline));
369 }
370
371 my %opendevfds = ();
372
373 # select the OBD device we talk to
374 sub Device {
375     my $device = shift;
376
377     if ( ! $device && ! $::device ) { # first time ever
378         $device = '/dev/obd0';
379     }
380
381     if (($device) && ($::device ne $device)) {
382         local *NEW_OBD;
383         my $newfd;
384
385         if ($::client_id) {
386             print "Disconnecting active session ($::client_id)...";
387             Disconnect($::client_id);
388         }
389
390         if ($opendevfds{$device}) {
391             $::dev_obd = $opendevfds{$device};
392         }
393         else {
394             # Open the device, as we need an FD for the ioctl
395             if (!sysopen(NEW_OBD, $device, 0)) {
396                 print "Cannot open $device. Did you insert the obdclass module ?\n";
397                 return -1;
398             }
399             print "Opened device $device\n";
400             $opendevfds{$device} = *NEW_OBD;
401             $::dev_obd = *NEW_OBD;
402         }
403         $::device = $device;    
404     }
405     print "Current device is $::device\n";
406     return 0;
407 }
408
409 sub Close {
410     my $device = shift;
411     my $fd2close;
412
413     if ( ! $device && ! $::device ) { # first time ever
414         print "Nothing to close\n";
415         return -1;
416     }
417
418     if ( ! $device ) {
419         $device = $::device;
420     }
421
422     if ($::device eq $device) {
423         if ($::client_id) {
424             print "Disconnecting active session ($::client_id)...";
425             Disconnect($::client_id);
426         }
427     }
428
429     $fd2close = $opendevfds{$device};
430     if ($fd2close) { # XXXX something wrong in this if statement
431         close ($fd2close);
432         $opendevfds{$device} = undef;
433         print "Closed device $device\n";
434     }
435     else {
436         print "Device $device was not open\n";
437         return -1;
438     }
439     
440     if ($::device eq $device) {
441         $::dev_obd = undef;
442         $::device = undef;
443     }
444     print "No current device. You just closed the current device ($device).\n";
445     return 0; 
446 }   
447  
448 sub Script {
449     my $cmdfilename = shift;
450     my $rc = 0;
451     if ( ! $cmdfilename )  {
452         print "please specify a command file name\n";
453         return -1;
454     }
455     if (! open(CMDF, $cmdfilename)) {
456         print "Cannot open $cmdfilename: $!\n";
457         return -1;
458     }
459     while (<CMDF>) {
460         if (/^#/) {
461             next;
462         }
463         print "execute> $_";
464         $rc = execute_line($_);
465         if ($rc != 0) {
466             print "Something went wrong .......command exit status: $rc\n";
467             last;
468         }
469     }
470     close(CMDF);
471     return $rc;
472 }
473
474 sub Shell {
475     my $user_shell=$ENV{'SHELL'};
476     print "% $user_shell -c '@_'\n";
477     if ( ! @_ ) {
478         print "please specify a shell command\n";
479         return;
480     }
481     system("$user_shell -c '@_'");
482     return ($? >> 8);
483 }
484   
485 sub Status {
486     my $oldfh = select(STDOUT);
487     $| = 1;
488
489     system('cat /proc/lustre/obd/*/status');
490     my $rc = ($? >> 8);
491
492     select($oldfh);
493     $| = 0;
494
495     return $rc;
496 }
497
498 sub Procsys {
499     my $set_sysobd = shift;
500     my $value = shift;
501
502     foreach $i (0 .. $#procsysobd_objects) {
503         my $sysobd = $procsysobd_objects[$i];
504
505         if (defined $set_sysobd) {
506             if ($sysobd ne $set_sysobd) { next; }
507
508             if (defined $value) { # set this one
509                 system("echo \"$value\" > /proc/sys/obd/$sysobd");
510             }
511             system("echo \"/proc/sys/obd/$sysobd:\"; cat /proc/sys/obd/$sysobd");
512             last;
513         }
514         else {
515             system("echo \"/proc/sys/obd/$sysobd:\"; cat /proc/sys/obd/$sysobd");
516         }
517     }
518     return ($? >> 8);
519 }
520
521 sub Insmod {
522     my $module = shift;
523     system("insmod $module");
524     return ($? >> 8);
525 }
526
527 sub Rmmod {
528     my $module = shift;
529     system("rmmod $module");
530     return ($? >> 8);
531 }
532
533 sub Lsmod {
534     my $module = shift;
535     system("lsmod $module");
536     return ($? >> 8);
537 }
538
539 sub Attach {
540     my $err = 0;
541     my $type = shift;
542     my $data;
543     my $datalen = 0;
544
545     if ( ! $type ) {
546         print "error: missing type\n";
547 usage:
548         print "usage: attach {obdext2 | obdsnap | obdscsi | obdtrace }\n";
549         return -1;
550     }
551
552     if ($type eq "obdscsi" ) {
553         my $adapter = shift;
554         my $bus = shift;
555         my $tid = shift;
556         my $lun = shift;
557
558         $data = pack("iiii", $adapter, $bus, $tid, $lun);
559         $datalen = 4 * 4;
560     } elsif ($type eq "obdsnap" ) {
561         my $snapdev = shift;
562         my $snapidx = shift;
563         my $tableno = shift;
564
565         $data = pack("iii", $snapdev, $snapidx, $tableno);
566         $datalen = 3 * 4;
567     } elsif ($type eq "obdext2") {
568         $data = pack("i", 4711);   # bogus data
569         $datalen = 4;
570     } elsif ($type eq "obdtrace") {
571         $data = pack("i", 4711);   # bogus data
572         $datalen = 4;
573     } else {
574         print "error: unknown attach type $type\n";
575         goto usage;
576     }
577
578     my $len = length($type);
579     my $cl = length($data);
580
581     print "type $type (len $len), datalen $datalen ($cl)\n";
582     my $packed = pack("Lipip", $::client_id, length($type), $type, $datalen, $data);
583
584     if (! defined $::dev_obd) {
585         print "No current device.\n";
586         return -1;
587     }
588     my $rc = ioctl($::dev_obd, &OBD_IOC_ATTACH, $packed);
589
590     if (!defined $rc) {
591         print STDERR "ioctl failed: $!\n";
592         return -1;
593     } elsif ($rc eq "0 but true") {
594         print "Finished (success)\n";
595         return 0;
596     } else {
597         print "ioctl returned error code $rc.\n";
598         return -1;
599     }
600 }
601
602
603 sub Detach {
604     my $err = 0;
605     my $data = "";
606
607     if (! defined $::dev_obd) {
608         print "No current device.\n";
609         return -1;
610     }
611
612     my $rc = ioctl($::dev_obd, &OBD_IOC_DETACH, $data);
613
614     if (!defined $rc) {
615         print STDERR "ioctl failed: $!\n";
616         return -1;
617     } elsif ($rc eq "0 but true") {
618         print "Finished (success)\n";
619         return 0;
620     } else {
621         print "ioctl returned error code $rc.\n";
622         return -1;
623     }
624 }
625
626
627 sub TestExt2Iterator { 
628     if (!defined($::client_id)) {
629         print "You must first ``connect''.\n";
630         return;
631     }
632
633     my $err = 0;
634     my $type = "obdext2";
635  
636     $data = pack("i", 4711); # bogus data
637     $datalen = 4;
638
639     my $len = length($type);
640     my $cl = length($data);
641     print "type $type (len $len), datalen $datalen ($cl)\n";
642     my $packed = pack("Lipip", $::client_id, length($type), $type, $datalen, $data);
643     if (! defined $::dev_obd) {
644         print "No current device.\n";
645         return -1;
646     }
647
648     my $rc = ioctl($::dev_obd, &OBD_EXT2_RUNIT, $packed);
649
650     if (!defined $rc) {
651         print STDERR "ioctl failed: $!\n";
652         return -1;
653     } elsif ($rc eq "0 but true") {
654         print "Finished (success)\n";
655         return 0;
656     } else {
657         print "ioctl returned error code $rc.\n";
658         return -1;
659     }
660 }
661
662
663 sub SnapDelete { 
664     if (!defined($::client_id)) {
665         print "You must first ``connect''.\n";
666         return -1;
667     }
668
669     my $err = 0;
670     my $type = "obdsnap";
671  
672     $data = pack("i", 4711); # bogus data
673     $datalen = 4;
674
675     my $len = length($type);
676     my $cl = length($data);
677     print "type $type (len $len), datalen $datalen ($cl)\n";
678     my $packed = pack("Lipip", $::client_id, length($type), $type, $datalen, $data);
679
680     # XXX We need to fix this up so that after the objects in this snapshot
681     #     are deleted, the snapshot itself is also removed from the table.
682
683     if (! defined $::dev_obd) {
684         print "No current device.\n";
685         return -1;
686     }
687
688     my $rc = ioctl($::dev_obd, &OBD_SNAP_DELETE, $packed);
689
690     if (!defined $rc) {
691         print STDERR "ioctl failed: $!\n";
692         return -1;
693     } elsif ($rc eq "0 but true") {
694         print "Finished (success)\n";
695         return 0;
696     } else {
697         print "ioctl returned error code $rc.\n";
698         return -1;
699     }
700 }
701
702
703 #      this routine does the whole job
704 sub SnapRestore { 
705     my $restoreto = shift;
706     my $snaptable = shift;
707     my $tableno = shift;
708     my $restoretime;
709
710     # don't do anything until connected
711     if (!defined($::client_id)) {
712         print "You must first ``connect''.\n";
713         return -1;
714     }
715
716     if ( ! $snaptable || ! defined $restoreto ) {
717         print "Usage: snaprestore \"restore to slot\" \"snaptable\" \"tableno\"\n";
718         return -1;
719     }
720
721     if ( ! -f $snaptable ) {
722         print "Table $snaptable doesn't exist\n";
723         return -1;
724     }
725    
726     my $table = ReadSnapShotTable($snaptable);
727     $restoretime = FindSnapInTable($table, $restoreto);
728     if ( ! defined $table->{0} || ! defined $restoretime ) {
729         PrintSnapShotTable($table);
730         print "No current or $restoreto slot in this table\n";
731         return -1;
732     }
733
734     my $currentindex = $table->{0};
735     if (  $table->{$restoretime} == $currentindex ) {
736         print "You should not restore to the current snapshot\n";
737         return -1;
738     }
739     
740     # swap the entries for 0 and $restoreto
741     my $tmp = $table->{$restoretime};
742     $table->{$restoretime} = $table->{0};
743     $table->{0} = $tmp;
744     # PrintSnapShotTable($table);
745
746     # write it back
747     WriteSnapShotTable($snaptable, $table);
748
749     # set it in the kernel
750     SnapSetTable($tableno, $snaptable);
751
752     # ready for the ioctl
753     my $err = 0;
754     my $type = "obdsnap";
755     $data = pack("i", $currentindex); # slot of previous current snapshot 
756     $datalen = 4;
757
758     my $len = length($type);
759     my $cl = length($data);
760     my $packed = pack("Lipip", $::client_id, length($type), $type, $datalen, $data);
761     if (! defined $::dev_obd) {
762         print "No current device.\n";
763         return -1;
764     }
765
766     my $rc = ioctl($::dev_obd, &OBD_SNAP_RESTORE, $packed);
767
768     if (!defined $rc) {
769         print STDERR "ioctl failed: $!\n";
770         return -1;
771     } elsif ($rc eq "0 but true") {
772         print "Snaprestore finished (success)\n";
773         delete $table->{$restoretime} if defined $restoretime;
774         # write it back
775         WriteSnapShotTable($snaptable, $table);
776         
777         # set it in the kernel
778         SnapSetTable($tableno, $snaptable);
779         # PrintSnapShotTable($table);
780         return 0;
781     } else {
782         print "ioctl returned error code $rc.\n";
783         return -1;
784     }
785 }
786
787 sub FindSnapInTable { 
788     my $table = shift;
789     my $snapno =shift;
790
791     foreach my $restoretime ( keys %{$table} ) {
792         if ( $table->{$restoretime} == $snapno) { 
793             print "Found key $restoretime for snapno $snapno\n";
794             return $restoretime;
795         }
796     }
797     undef;
798 }
799             
800
801 sub SnapPrint { 
802     my $err = 0;
803     my $type = "obdsnap";
804     my $snaptableno = shift;
805
806     $data = pack("i", $snaptableno);
807     $datalen = 4;
808
809     my $len = length($type);
810     my $cl = length($data);
811     print "type $type (len $len), datalen $datalen ($cl)\n";
812     my $packed = pack("Lipip", $::client_id, length($type), $type, $datalen, $data);
813     if (! defined $::dev_obd) {
814         print "No current device.\n";
815         return -1;
816     }
817
818     my $rc = ioctl($::dev_obd, &OBD_SNAP_PRINTTABLE, $packed);
819
820     if (!defined $rc) {
821         print STDERR "ioctl failed: $!\n";
822         return -1;
823     } elsif ($rc eq "0 but true") {
824         print "Finished (success)\n";
825         return 0;
826     } else {
827         print "ioctl returned error code $rc.\n";
828         return -1;
829     }
830 }
831
832 sub SnapSetTable {
833     my $err = 0;
834     my $type = "obdsnap";
835     my $snaptableno = shift;
836     my $file = shift;
837     my $snapcount;
838     my $table = {};
839     my $data;
840     my $datalen = 0;
841
842     if ( ! -f $file ) {
843         print "No such file $file\n";
844         return -1;
845     }
846
847     $table = ReadSnapShotTable($file);
848
849     $snapcount = keys %{$table};
850     print "Snapcount $snapcount\n";
851
852     if ( ! defined $table->{0} ) {
853         print "No current snapshot in table! First make one\n";
854         return -1;
855     }
856     $data = pack("ii", $snaptableno, $snapcount);
857     $datalen = 2 * 4;
858     foreach my $time (sort keys %{$table}) {
859         # XXX we should change to pack LL instead of I for times
860         $data .= pack("Ii", $time, $table->{$time});
861         $datalen += 8;
862     }
863
864     my $len = length($type);
865     my $cl = length($data);
866     print "type $type (len $len), datalen $datalen ($cl)\n";
867     my $packed = pack("Lipip", $::client_id, length($type), $type, $datalen, $data);
868     if (! defined $::dev_obd) {
869         print "No current device.\n";
870         return -1;
871     }
872
873     my $rc = ioctl($::dev_obd, &OBD_SNAP_SETTABLE, $packed);
874
875     if (!defined $rc) {
876         print STDERR "ioctl failed: $!\n";
877         return -1;
878     } elsif ($rc eq "0 but true") {
879         print "Finished (success)\n";
880         return 0;
881     } else {
882         print "ioctl returned error code $rc.\n";
883         return -1;
884     }
885 }
886
887
888 sub SnapShotTable  {
889
890     my $file = &readl("enter file name: ");
891     if ( ! -f $file ) {
892         `touch $file`;
893     }
894     my $table = ReadSnapShotTable($file);
895   
896   again:
897     PrintSnapShotTable($table);
898     my $action = &readl("Add, Delete or Quit [adq]: ");
899     goto done if ($action  =~ "^q.*" );
900     goto add if ($action =~ "^a.*");
901     goto del  if ($action =~ "^d.*");
902     goto again;
903
904   add:
905     my $idx = &readl("enter index where you want this snapshot: ");
906     my $time = &readl("enter time or 'now' or 'current': ");
907     my $oldtime = SnapFindTimeFromIdx($idx, $table);
908     if (defined $oldtime) {
909         print "This already exists, first clean up\n";
910         goto again;
911     }
912
913     if ( $time  eq 'now' ) {
914         $time = time;
915     } elsif ( $time eq 'current' ) { 
916         $time = 0;
917     }
918     $table->{$time} = $idx;
919     goto again;
920
921   del:
922     $didx = &readl("Enter index to delete: ");
923     my $deltime = SnapFindTimeFromIdx($didx, $table);
924     delete $table->{$deltime} if defined $deltime;
925     goto again;
926
927   done:
928     my $ok = &readl("OK with new table? [Yn]: ");
929     unless ( $ok eq "n" )  {
930         WriteSnapShotTable($file, $table);
931     }
932     return 0;
933 }
934
935 sub SnapFindTimeFromIdx {
936     my $idx = shift;
937     my $table = shift;
938
939     foreach my $time ( keys %{$table} ) {
940         if ( $table->{$time} == $idx ) {
941             return $time;
942         }
943     }
944     undef;
945 }
946
947 sub PrintSnapShotTable {
948     my $table = shift;
949     my $time;
950     
951     foreach  $time ( sort keys %{$table} ) {
952         my $stime = localtime($time);
953         if ( ! $time ) { 
954             $stime = "current";
955         }
956         printf "Time: %s -- Index %d\n", $stime, $table->{$time};
957     }
958 }
959
960 sub ReadSnapShotTable {
961
962     my $file = shift;
963     my $table = {};
964
965     open FH, "<$file";
966     while ( <FH> ) {
967         my ($time, $index) = split ;
968         $table->{$time} = $index;
969     }
970     close FH;
971
972     PrintSnapShotTable($table);
973
974     return $table;
975 }
976
977 sub WriteSnapShotTable {
978     my $file = shift;
979     my $table = shift;
980
981     open FH, ">$file";
982     foreach my $time ( sort keys %{$table}  ) {
983         print FH "$time $table->{$time}\n";
984     }
985     close FH;
986 }
987
988 sub Copy {
989     my $err = 0;
990     my $src_obdo;
991     my $dst_obdo;
992
993     # Note: _copy IOCTL takes parameters as dst, src.
994     #       Copy function takes parameters as src, dst.
995     $src_obdo->{id} = shift;
996     $dst_obdo->{id} = shift;
997     $src_obdo->{valid} = &OBD_MD_FLALL;
998
999     # XXX need to fix copy so we can have 2 client IDs here
1000     my $packed = pack("L", $::client_id) . obdo_pack($dst_obdo) . pack("L", $::client_id) . obdo_pack($src_obdo);
1001     if (! defined $::dev_obd) {
1002         print "No current device.\n";
1003         return -1;
1004     }
1005
1006     my $rc = ioctl($::dev_obd, &OBD_IOC_COPY, $packed);
1007
1008     if (!defined $rc) {
1009         print STDERR "ioctl failed: $!\n";
1010         return -1;
1011     } elsif ($rc eq "0 but true") {
1012         print "Finished (success)\n";
1013         return 0;
1014     } else {
1015         print "ioctl returned error code $rc.\n";
1016         return -1;
1017     }
1018 }
1019
1020 sub Migrate {
1021     my $err = 0;
1022
1023     # Note: _migr IOCTL takes parameters as dst, src.
1024     #       Migrate function takes parameters as src, dst.
1025     $src_obdo->{id} = shift;
1026     $dst_obdo->{id} = shift;
1027     $src_obdo->{valid} = &OBD_MD_FLALL;
1028
1029     # We pack a dummy connection ID here
1030     my $packed = pack("L", $::client_id) . obdo_pack($dst_obdo) . pack("L", $::client_id) . obdo_pack($src_obdo);
1031     if (! defined $::dev_obd) {
1032         print "No current device.\n";
1033         return -1;
1034     }
1035
1036     my $rc = ioctl($::dev_obd, &OBD_IOC_MIGR, $packed);
1037
1038     if (!defined $rc) {
1039         print STDERR "ioctl failed: $!\n";
1040         return -1;
1041     } elsif ($rc eq "0 but true") {
1042         print "Finished (success)\n";
1043         return 0;
1044     } else {
1045         print "ioctl returned error code $rc.\n";
1046         return -1;
1047     }
1048 }
1049
1050
1051 sub Format {
1052     my $err = 0;
1053     my $size = shift;
1054     my $data = pack("i", $size);
1055     my $datalen = 4;
1056
1057     my $packed = pack("ip", $datalen, $data);
1058     if (! defined $::dev_obd) {
1059         print "No current device.\n";
1060         return -1;
1061     }
1062     my $rc = ioctl($::dev_obd, &OBD_IOC_FORMATOBD, $packed);
1063
1064     if (!defined $rc) {
1065         print STDERR "ioctl failed: $!\n";
1066         return -1;
1067     } elsif ($rc eq "0 but true") {
1068         print "Finished (success)\n";
1069         return 0;
1070     } else {
1071         print "ioctl returned error code $rc.\n";
1072         return -1;
1073     }
1074 }
1075
1076 sub Partition {
1077     my $err = 0;
1078     my $partno = shift;
1079     my $size = shift;
1080     my $data = pack("ii", $partno, $size);
1081     my $datalen = 2 * 4;
1082
1083     my $packed = pack("ip", $datalen, $data);
1084     if (! defined $::dev_obd) {
1085         print "No current device.\n";
1086         return -1;
1087     }
1088     my $rc = ioctl($::dev_obd, &OBD_IOC_PARTITION, $packed);
1089
1090     if (!defined $rc) {
1091         print STDERR "ioctl failed: $!\n";
1092         return -1;
1093     } elsif ($rc eq "0 but true") {
1094         print "Finished (success)\n";
1095         return 0;
1096     } else {
1097         print "ioctl returned error code $rc.\n";
1098         return -1;
1099     }
1100 }
1101
1102 sub Setup {
1103     my $err = 0;
1104     my $arg = shift;
1105     my $data;
1106     my $datalen = 0;
1107
1108     # XXX we need a getinfo ioctl to validate parameters 
1109     # by type here
1110
1111     if ($arg && !defined($::st = stat($arg))) {
1112             print "$arg is not a valid device\n";
1113             return -1;
1114     }
1115
1116     printf "setting up %s, device %x\n", $arg, $::st->rdev();
1117     if ( $arg ) {
1118         $data = $arg;
1119         $datalen = length($arg)+1; # need null character also
1120     }
1121
1122     my $packed = pack("iip", $datalen, $::st->rdev(), $data);
1123     if (! defined $::dev_obd) {
1124         print "No current device.\n";
1125         return -1;
1126     }
1127     my $rc = ioctl($::dev_obd, &OBD_IOC_SETUP, $packed);
1128
1129     if (!defined $rc) {
1130         print STDERR "ioctl failed: $!\n";
1131         return -1;
1132     } elsif ($rc eq "0 but true") {
1133         print "Finished (success)\n";
1134         return 0;
1135     } else {
1136         print "ioctl returned error code $rc.\n";
1137         return -1;
1138     }
1139 }
1140
1141 sub Cleanup {
1142     my $err = "0";
1143     if (! defined $::dev_obd) {
1144         print "No current device.\n";
1145         return -1;
1146     }
1147     my $rc = ioctl($::dev_obd, &OBD_IOC_CLEANUP, $err);
1148
1149     if (!defined $rc) {
1150         print STDERR "ioctl failed: $!\n";
1151         return -1;
1152     } elsif ($rc eq "0 but true") {
1153         print "Finished (success)\n";
1154         $::client_id = 0;
1155         return 0;
1156     } else {
1157         print "ioctl returned error code $rc.\n";
1158         return -1;
1159     }
1160 }
1161
1162
1163 sub Connect {
1164     my $rc;
1165
1166     my $packed = "";
1167     if (! defined $::dev_obd) {
1168         print "No current device.\n";
1169         return -1;
1170     }
1171     $rc = ioctl($::dev_obd, &OBD_IOC_CONNECT, $packed);
1172     $id = unpack("I", $packed);
1173
1174     if (!defined $rc) {
1175         print STDERR "ioctl failed: $!\n";
1176         return -1;
1177     } elsif ($rc eq "0 but true") {
1178         $::client_id = $id;
1179         print "Client ID     : $id\n";
1180         print "Finished (success)\n";
1181         return 0;
1182     } else {
1183         print "ioctl returned error code $rc.\n";
1184         return -1;
1185     }
1186 }
1187
1188 sub Disconnect {
1189     my $id = shift;
1190
1191     if (!defined($id)) {
1192         $id = $::client_id;
1193     }
1194
1195     if (!defined($id)) {
1196         print "syntax: disconnect [client ID]\n";
1197         print "When client ID is not given, the last valid client ID to be returned by a\n";
1198         print "connect command this session is used; there is no such ID.\n";
1199         return -1;
1200     }
1201
1202     my $packed = pack("L", $id);
1203     if (! defined $::dev_obd) {
1204         print "No current device.\n";
1205         return -1;
1206     }
1207     my $rc = ioctl($::dev_obd, &OBD_IOC_DISCONNECT, $packed);
1208
1209     if (!defined $rc) {
1210         print STDERR "ioctl failed: $!\n";
1211         return -1;
1212     } elsif ($rc eq "0 but true") {
1213         $::client_id = undef;
1214         print "Finished (success)\n";
1215         return 0;
1216     } else {
1217         print "ioctl returned error code $rc.\n";
1218         return -1;
1219     }
1220 }
1221
1222 sub Create {
1223     if (!defined($::client_id)) {
1224         print "You must first ``connect''.\n";
1225         return -1;
1226     }
1227
1228     my $num = shift;
1229     my $mode = shift;
1230     my $quiet = shift;
1231     my $rc;
1232     my $prealloc = 0;
1233
1234     if (!defined($num)) {
1235         $num = 1;
1236     }
1237
1238     if (!defined($mode)) {
1239         $mode = 0100644;         # create a file (rw-r--r--) if not specified
1240     }
1241
1242     if (scalar($num) < 1 || defined($quiet) && $quiet ne "quiet") {
1243         print "usage: create [<number of objects> [<mode> [quiet]]]\n";
1244         return -1;
1245     }
1246
1247     my $i;
1248     my $id = 0;                 # can't currently request IDs
1249
1250     print "Creating " . scalar($num) . " object";
1251     if (scalar($num) > 1) {
1252         print "s";
1253     }
1254     print "\n";
1255
1256     for ($i = 0; $i < scalar($num); $i++) {
1257         my $obdo;
1258         $obdo->{id} = $id;
1259         $obdo->{mode} = scalar($mode);
1260         $obdo->{valid} = &OBD_MD_FLMODE;
1261
1262         my $packed = pack("I", $::client_id) . obdo_pack($obdo);
1263         if (! defined $::dev_obd) {
1264             print "No current device.\n";
1265             return -1;
1266         }
1267         $rc = ioctl($::dev_obd, &OBD_IOC_CREATE, $packed);
1268         if ($rc ne "0 but true") {
1269             last;
1270         } elsif (!defined($quiet)) {
1271             $obdo = obdo_unpack($packed, 4);
1272             print "Created object #$obdo->{id}.\n";
1273         }
1274     }
1275
1276     if (!defined $rc) {
1277         print STDERR "ioctl failed: $!\n";
1278         return -1;
1279     } elsif ($rc eq "0 but true") {
1280         print "Finished (success)\n";
1281         return 0;
1282     } else {
1283         print "ioctl returned error code $rc.\n";
1284         return -1;
1285     }
1286 }
1287
1288 sub Sync {
1289     my $err = "0";
1290     if (! defined $::dev_obd) {
1291         print "No current device.\n";
1292         return -1;
1293     }
1294     my $rc = ioctl($::dev_obd, &OBD_IOC_SYNC, $err);
1295
1296     if (!defined $rc) {
1297         print STDERR "ioctl failed: $!\n";
1298         return -1;
1299     } elsif ($rc eq "0 but true") {
1300         print "Finished (success)\n";
1301         return 0;
1302     } else {
1303         print "ioctl returned error code $rc.\n";
1304         return -1;
1305     }
1306 }
1307
1308 sub Destroy {
1309     if (!defined($::client_id)) {
1310         print "You must first ``connect''.\n";
1311         return -1;
1312     }
1313
1314     my $id = shift;
1315
1316     if (!defined($id) || scalar($id) < 1) {
1317         print "usage: destroy <object number>\n";
1318         return -1;
1319     }
1320
1321     print "Destroying object $id...\n";
1322     my $packed = pack("IL", $::client_id, $id);
1323     if (! defined $::dev_obd) {
1324         print "No current device.\n";
1325         return -1;
1326     }
1327     my $rc = ioctl($::dev_obd, &OBD_IOC_DESTROY, $packed);
1328
1329     if (!defined $rc) {
1330         print STDERR "ioctl failed: $!\n";
1331         return -1;
1332     } elsif ($rc eq "0 but true") {
1333         print "Finished (success)\n";
1334         return 0;
1335     } else {
1336         print "ioctl returned error code $rc.\n";
1337         return -1;
1338     }
1339 }
1340
1341 sub Getattr {
1342     if (!defined($::client_id)) {
1343         print "You must first ``connect''.\n";
1344         return -1;
1345     }
1346
1347     my $id = shift;
1348
1349     if (!defined($id) || scalar($id) < 1) {
1350         print "invalid arguments; type \"help getattr\" for a synopsis\n";
1351         return -1;
1352     }
1353
1354     # see Setattr
1355     my $obdo;
1356     $obdo->{id} = $id;
1357     $obdo->{valid} = &OBD_MD_FLALL;
1358     my $packed = pack("L", $::client_id) . obdo_pack($obdo);
1359     if (! defined $::dev_obd) {
1360         print "No current device.\n";
1361         return -1;
1362     }
1363     my $rc = ioctl($::dev_obd, &OBD_IOC_GETATTR, $packed);
1364     
1365     if (!defined $rc) {
1366         print STDERR "ioctl failed: $!\n";
1367         return -1;
1368     } elsif ($rc eq "0 but true") {
1369         $obdo = obdo_unpack($packed,  4); 
1370         obdo_print($obdo);
1371         return 0;
1372     } else {
1373         print "ioctl returned error code $rc.\n";
1374         return -1;
1375     }
1376 }
1377
1378 sub Setattr {
1379     if (!defined($::client_id)) {
1380         print "You must first ``connect''.\n";
1381         return -1;
1382     }
1383
1384     my $id = shift;
1385
1386     if (!defined($id) || scalar($id) < 1) {
1387         print "invalid arguments; type \"help setattr\" for a synopsis\n";
1388         return -1;
1389     }
1390
1391     # XXX we do not currently set all of the fields in the obdo
1392     my $obdo;
1393     $obdo->{id} = $id;
1394     $obdo->{mode} = oct(shift);
1395     $obdo->{uid} = shift;
1396     $obdo->{gid} = shift;
1397     $obdo->{size} = shift;
1398     $obdo->{atime} = shift;
1399     $obdo->{mtime} = shift;
1400     $obdo->{ctime} = shift;
1401     $obdo->{valid} = 0;
1402
1403     if (defined($obdo->{atime})) {
1404         $obdo->{valid} |= &OBD_MD_FLATIME;
1405     }
1406     if (defined($obdo->{mtime})) {
1407         $obdo->{valid} |= &OBD_MD_FLMTIME;
1408     }
1409     if (defined($obdo->{ctime})) {
1410         $obdo->{valid} |= &OBD_MD_FLCTIME;
1411     }
1412     if (defined($obdo->{size})) {
1413         $obdo->{valid} |= &OBD_MD_FLSIZE;
1414     }
1415     if (defined($obdo->{mode})) {
1416         $obdo->{valid} |= &OBD_MD_FLMODE;
1417     }
1418     if (defined($obdo->{uid})) {
1419         $obdo->{valid} |= &OBD_MD_FLUID;
1420     }
1421     if (defined($obdo->{gid})) {
1422         $obdo->{valid} |= &OBD_MD_FLGID;
1423     }
1424
1425     printf "valid is %x, mode is %o\n", $obdo->{valid}, $obdo->{mode};
1426     my $packed = pack("L", $::client_id) . obdo_pack($obdo);
1427     if (! defined $::dev_obd) {
1428         print "No current device.\n";
1429         return -1;
1430     }
1431     my $rc = ioctl($::dev_obd, &OBD_IOC_SETATTR, $packed);
1432
1433     if (!defined $rc) {
1434         print STDERR "ioctl failed: $!\n";
1435         return -1;
1436     } elsif ($rc eq "0 but true") {
1437         print "Finished (success)\n";
1438         return 0;
1439     } else {
1440         print "ioctl returned error code $rc.\n";
1441         return -1;
1442     }
1443 }
1444
1445 sub Read {
1446     if (!defined($::client_id)) {
1447         print "You must first ``connect''.\n";
1448         return -1;
1449     }
1450
1451     my $id = shift;
1452     my $count = shift;
1453     my $offset = shift;
1454   
1455     if (!defined($id) || scalar($id) < 1 || !defined($count) ||
1456         $count < 1 || (defined($offset) && $offset < 0)) {
1457         print "invalid arguments; type \"help read\" for a synopsis\n";
1458         return -1;
1459     }
1460
1461     if (!defined($offset)) {
1462         $offset = 0;
1463     }
1464
1465     print("Reading $count bytes starting at byte $offset from object " .
1466           "$id...\n");
1467
1468     # "allocate" a large enough buffer
1469     my $buf = sprintf("%${count}s", " ");
1470     die "suck" if (length($buf) != $count);
1471
1472     my $obdo;
1473     $obdo->{id} = $id;
1474
1475     # the perl we're using doesn't support pack type Q, and offset is 64 bits
1476     my $packed = pack("L", $::client_id) . obdo_pack($obdo) .
1477                  pack("p LL LL", $buf, $count, $offset);
1478
1479     if (! defined $::dev_obd) {
1480         print "No current device.\n";
1481         return -1;
1482     }
1483     my $rc = ioctl($::dev_obd, &OBD_IOC_READ, $packed);
1484
1485     $retval = unpack("l", $packed);
1486
1487     if (!defined $rc) {
1488         print STDERR "ioctl failed: $!\n";
1489         return -1;
1490     } elsif ($rc eq "0 but true") {
1491         if ($retval >= 0) {
1492                 print substr($buf, 0, $retval);
1493                 print "\nRead $retval of an attempted $count bytes.\n";
1494                 print "Finished (success)\n";
1495                 return 0;
1496         } else {
1497                 print "Finished (error $retval)\n";
1498                 return $retval;
1499         }
1500     } else {
1501         print "ioctl returned error code $rc.\n";
1502         return -1;
1503     }
1504 }
1505
1506 sub Read2 {
1507     if (!defined($::client_id)) {
1508         print "You must first ``connect''.\n";
1509         return -1;
1510     }
1511
1512     my $id = shift;
1513     my $count = shift;
1514     my $offset = shift;
1515   
1516     if (!defined($id) || scalar($id) < 1 || !defined($count) ||
1517         $count < 1 || (defined($offset) && $offset < 0)) {
1518         print "invalid arguments; type \"help read\" for a synopsis\n";
1519         return -1;
1520     }
1521
1522     if (!defined($offset)) {
1523         $offset = 0;
1524     }
1525
1526     print("Reading $count bytes starting at byte $offset from object " .
1527           "$id...\n");
1528
1529     # "allocate" a large enough buffer
1530     my $buf = sprintf("%${count}s", " ");
1531     die "suck" if (length($buf) != $count);
1532
1533     my $obdo;
1534     $obdo->{id} = $id;
1535
1536     # the perl we're using doesn't support pack type Q, and offset is 64 bits
1537     my $packed = pack("L", $::client_id) . obdo_pack($obdo) .
1538                  pack("p LL LL", $buf, $count, $offset);
1539
1540     if (! defined $::dev_obd) {
1541         print "No current device.\n";
1542         return -1;
1543     }
1544     my $rc = ioctl($::dev_obd, &OBD_IOC_READ2, $packed);
1545
1546     $retval = unpack("l", $packed);
1547
1548     if (!defined $rc) {
1549         print STDERR "ioctl failed: $!\n";
1550         return -1;
1551     } elsif ($rc eq "0 but true") {
1552         if ($retval >= 0) {
1553                 print substr($buf, 0, $retval);
1554                 print "\nRead $retval of an attempted $count bytes.\n";
1555                 print "Finished (success)\n";
1556                 return 0;
1557          } else {
1558                 print "Finished (error $retval)\n";
1559                 return $retval;
1560         }
1561     } else {
1562         print "ioctl returned error code $rc.\n";
1563         return -1;
1564     }
1565 }
1566
1567 sub Write {
1568     if (!defined($::client_id)) {
1569         print "You must first ``connect''.\n";
1570         return -1;
1571     }
1572
1573     my $id = shift;
1574     my $offset = shift;
1575     my $text = join(' ', @_);
1576     my $count = length($text);
1577
1578     if (!defined($id) || scalar($id) < 1 || !defined($offset) ||
1579         scalar($offset) < 0) {
1580         print "invalid arguments; type \"help write\" for a synopsis\n";
1581         return -1;
1582     }
1583
1584     if (!defined($text)) {
1585         $text = "";
1586         $count = 0;
1587     }
1588
1589     print("Writing $count bytes starting at byte $offset to object $id...\n");
1590
1591     my $obdo;
1592     $obdo->{id} = $id;
1593
1594     # the perl we're using doesn't support pack type Q
1595     my $packed = pack("L", $::client_id) . obdo_pack($obdo) .
1596                  pack("p LL LL", $text, $count, $offset);
1597
1598     if (! defined $::dev_obd) {
1599         print "No current device.\n";
1600         return -1;
1601     }
1602     my $rc = ioctl($::dev_obd, &OBD_IOC_WRITE, $packed);
1603
1604     $retval = unpack("l", $packed);
1605
1606     if (!defined $rc) {
1607         print STDERR "ioctl failed: $!\n";
1608         return -1;
1609     } elsif ($rc eq "0 but true") {
1610         if ($retval >= 0) {
1611                 print "\nWrote $retval of an attempted $count bytes.\n";
1612                 print "Finished (success)\n";
1613                 return 0;
1614         } else {
1615                 print "Finished (error $retval)\n";
1616                 return $retval;
1617         }
1618     } else {
1619         print "ioctl returned error code $rc.\n";
1620         return -1;
1621     }
1622 }
1623
1624 sub Punch {
1625     if (!defined($::client_id)) {
1626         print "You must first ``connect''.\n";
1627         return -1;
1628     }
1629
1630     my $id = shift;
1631     my $start = shift;
1632     my $count = shift;
1633
1634     if (!defined($id) || scalar($id) < 1 || !defined($start) ||
1635         scalar($start) < 0 || !defined($count) || scalar($count) < 0) {
1636         print "invalid arguments; type \"help punch\" for a synopsis\n";
1637         return -1;
1638     }
1639
1640     print("Punching $count bytes starting at byte $start from object $id...\n");
1641
1642     my $obdo;
1643     $obdo->{id} = $id;
1644
1645     # the perl we're using doesn't support pack type Q
1646     my $packed = pack("L", $::client_id) . obdo_pack($obdo) .
1647                  pack("p LL LL", $buf, $start, $count);
1648
1649     if (! defined $::dev_obd) {
1650         print "No current device.\n";
1651         return -1;
1652     }
1653     my $rc = ioctl($::dev_obd, &OBD_IOC_PUNCH, $packed);
1654
1655     $retval = unpack("l", $packed);
1656
1657     if (!defined $rc) {
1658         print STDERR "ioctl failed: $!\n";
1659     } elsif ($rc eq "0 but true") {
1660         if ($retval >= 0) {
1661                 print "\nPunched $retval of an attempted $count bytes.\n";
1662                 print "Finished (success)\n";
1663                 return 0;
1664         } else {
1665                 print "Finished (error $retval)\n";
1666                 return $retval;
1667         }
1668     } else {
1669         print "ioctl returned error code $rc.\n";
1670         return -1;
1671     }
1672 }
1673
1674 sub Preallocate {
1675     my $num = shift;
1676
1677     if (!defined($::client_id)) {
1678         print "You must first ``connect''.\n";
1679         return -1;
1680     }
1681
1682     if (!defined($num) || scalar($num) < 1 || scalar($num) > 32) {
1683         $num = 32;
1684     }
1685
1686     print "Preallocating $num objects...\n";
1687     # client id, alloc, id[32]
1688     my $packed = pack("LLx128", $::client_id, $num);
1689
1690     if (! defined $::dev_obd) {
1691         print "No current device.\n";
1692         return -1;
1693     }
1694     my $rc = ioctl($::dev_obd, &OBD_IOC_PREALLOCATE, $packed);
1695
1696     if (!defined $rc) {
1697         print STDERR "ioctl failed: $!\n";
1698         return -1;
1699     } elsif ($rc eq "0 but true") {
1700         my $alloc = unpack("x4L", $packed);
1701         my @ids = unpack("x8L32", $packed);
1702         my $i;
1703
1704         print "Got $alloc objects: ";
1705         foreach $i (@ids) {
1706             print $i . " ";
1707         }
1708         print "\nFinished (success)\n";
1709         return 0;
1710     } else {
1711         print "ioctl returned error code $rc.\n";
1712         return -1;
1713     }
1714 }
1715
1716 sub Decusecount {
1717     if (! defined $::dev_obd) {
1718         print "No current device.\n";
1719         return -1;
1720     }
1721     my $rc = ioctl($::dev_obd, &OBD_IOC_DEC_USE_COUNT, 0);
1722
1723     if (!defined $rc) {
1724         print STDERR "ioctl failed: $!\n";
1725         return -1;
1726     } elsif ($rc eq "0 but true") {
1727         print "Finished (success)\n";
1728         return 0;
1729     } else {
1730         print "ioctl returned error code $rc.\n";
1731         return -1;
1732     }
1733 }
1734
1735 sub Statfs {
1736     if (!defined($::client_id)) {
1737         print "You must first ``connect''.\n";
1738         return -1;
1739     }
1740
1741     # struct statfs {
1742     #         long f_type;
1743     #         long f_bsize;
1744     #         long f_blocks;
1745     #         long f_bfree;
1746     #         long f_bavail;
1747     #         long f_files;
1748     #         long f_ffree;
1749     #         __kernel_fsid_t f_fsid; (64 bits)
1750     #         long f_namelen;
1751     #         long f_spare[6];
1752     # };
1753
1754     my $packed = pack("LLLLLLLIILL6", $::client_id, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1755                       0, 0, 0, 0, 0, 0);
1756
1757     if (! defined $::dev_obd) {
1758         print "No current device.\n";
1759         return -1;
1760     }
1761     my $rc = ioctl($::dev_obd, &OBD_IOC_STATFS, $packed);
1762
1763     if (!defined $rc) {
1764         print STDERR "ioctl failed: $!\n";
1765         return -1;
1766     } elsif ($rc eq "0 but true") {
1767         # skip both the conn_id and the fs_type in the buffer
1768         my ($bsize, $blocks, $bfree, $bavail, $files, $ffree) =
1769             unpack("x4x4LLLLLL", $packed);
1770         print("$bsize byte blocks: $blocks, " . ($blocks - $bfree) . " used, " .
1771               "$bfree free ($bavail available).\n");
1772         print "$files files, " . ($files - $ffree) . " used, $ffree free.\n";
1773         print "Finished (success)\n";
1774         return 0;
1775     } else {
1776         print "ioctl returned error code $rc.\n";
1777         return -1;
1778     }
1779 }
1780
1781 sub Help {
1782     my $cmd = shift;
1783
1784     if ( !$cmd || !$commands{$cmd} ) {
1785         print "Comands: ", join( ' ', @jcm_cmd_list), "\n";
1786     } else {
1787         print "Usage: " .  $commands{$cmd}->{doc} . "\n";
1788     }
1789     return 0;
1790 }
1791
1792 sub Quit {
1793     if ($::client_id) {
1794         print "Disconnecting active session ($::client_id)...";
1795         Disconnect($::client_id);
1796     }
1797     exit;
1798 }