Whamcloud - gitweb
Updated snapshot indexes in superblock to not depend on index "0" as being
[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
65 eval 'sub ATTR_MODE () {1;}' unless defined(&ATTR_MODE);
66 eval 'sub ATTR_UID () {2;}' unless defined(&ATTR_UID);
67 eval 'sub ATTR_GID () {4;}' unless defined(&ATTR_GID);
68 eval 'sub ATTR_SIZE () {8;}' unless defined(&ATTR_SIZE);
69 eval 'sub ATTR_ATIME () {16;}' unless defined(&ATTR_ATIME);
70 eval 'sub ATTR_MTIME () {32;}' unless defined(&ATTR_MTIME);
71 eval 'sub ATTR_CTIME () {64;}' unless defined(&ATTR_CTIME);
72
73 use Getopt::Long;
74 use File::stat;
75 use Storable;
76 use Carp;
77 use Term::ReadLine;
78 use IO::Handle;
79
80
81 my ($file);
82
83 GetOptions("f!" => \$file, "device=s" => \$::device, "fs=s" => $::filesystem) || die "Getoptions";
84
85
86 # get a console for the app
87
88 my $line;
89 my $command;
90 my $arg;
91
92 my %commands =
93     ('device' => {func => "Device", doc => "device <dev>: open another OBD device"},
94      'filesystem' => {func => "Filesystem", doc => "filesystem <dev>: partition for direct OBD device"},
95      'create' => {func => "Create", doc => "create: creates a new inode"},
96      'attach' => {func => "Attach", doc => "attach type [adapter bus tid lun]"},
97      'detach' => {func => "Detach", doc => "detach this device"},
98      'snapattach' => {func => "SnapAttach", doc => "snapattach snapno snapcount time1 index1 .... timek indexk"},
99      'snaptable' => {func => "SnapShotTable", doc => "snaptable: build a snapshot table (interactive)"},
100      'copy' => {func => "Copy", doc => "copy srcid tgtid"},
101      'migrate' => {func => "Migrate", doc => "migrate srcid tgtid"},
102      'format' => {func => "Format", doc => "format type adapter bus tid lun size"},
103      'partition' => {func => "Partition", doc => "partition type adapter bus tid lun partition size"},
104      'setup' => {func => "Setup", doc => "setup: link the ext2 partition (default /dev/loop0) to this obddev"},
105      'connect' => {func => "Connect", doc => "connect: allocates client ID for this session"},
106      'disconnect' => {func => "Disconnect", doc => "disconnect [id]: frees client resources"},
107      'sync' => {func => "Sync", doc => "sync: flushes buffers to disk"},
108      'destroy' => {func => "Destroy", doc => "setup: destroys an inode"},
109      'cleanup' => {func => "Cleanup", doc => "cleanup the minor obd device"},
110      'dec_use_count' => {func => "Decusecount", doc => "decreases the module use count so that the module can be removed following an oops"},
111      'read' => {func => "Read", doc => "read <inode> <count> [offset]"},
112      'fsread' => {func => "Read2", doc => "read <inode> <count> [offset]"},
113      'write' => {func => "Write", doc => "write <inode> <offset> <text>"},
114      'setattr' => {func => "Setattr", doc => "setattr <inode> [mode [uid [gid [size [atime [mtime [ctime]]]]]]]"},
115      'getattr' => {func => "Getattr", doc => "getattr <inode>: displays inode object attributes"},
116      'preallocate' => {func => "Preallocate", doc => "preallocate [num]: requests preallocation of num inodes."},
117      'statfs' => {func => "Statfs", doc => "statfs: filesystem status information"},
118      'help' => {func => \&Help,  doc => "help: this message"},
119      'quit' => {func => \&Quit,  doc => "see \"exit\""},
120      'exit' => {func => \&Quit,  doc => "see \"quit\""}
121     );
122
123 #
124 #       setup completion function
125 #
126 my @jcm_cmd_list = keys %commands;
127
128 my $term, $attribs;
129
130
131 # Get going....
132
133 Device($::device);
134 Filesystem($::filesystem);
135
136 sub readl {
137     if ( $file ) {
138         my $str = <STDIN>;
139         chop($str);
140         return $str;
141   } else {
142       return $term->readline(@_);
143   }
144 }
145
146
147
148 if ( $file ) {
149         while ( <STDIN> ) {
150             print $_;
151             execute_line($_);
152         }
153         exit 0;
154 } else {
155     $term = new Term::ReadLine 'obdcontrol ';
156     $attribs = $term->Attribs;
157     $attribs->{attempted_completion_function} = \&completeme;
158     $term->ornaments('md,me,,');        # bold face prompt
159     
160     # make sure stdout is not buffered
161     STDOUT->autoflush(1);
162
163
164     # Get on with the show
165     process_line();
166 }
167
168 #------------------------------------------------------------------------------
169 sub completeme {
170     my ($text, $line, $start, $end) = @_;
171     if (substr($line, 0, $start) =~ /^\s*$/) {
172         $attribs->{completion_word} = \@jcm_cmd_list;
173         return $term->completion_matches($text,
174                                          $attribs->{'list_completion_function'});
175     }
176 }
177
178 sub find_command {
179     my $given = shift;
180     my $name;
181     my @completions = completeme($given, $given, 0, length($given));
182     if ($#completions == 0) {
183         $name = shift @completions;
184     }
185
186     return $name;
187 }
188
189 # start making requests
190 sub process_line {
191   foo:
192     $line = $term->readline("obdcontrol > ");
193     execute_line($line);
194     goto foo;
195 }
196
197 sub execute_line {
198     my $line = shift;
199
200     my @arg = split(' ', $line);
201     my $word = shift @arg;
202
203     my $cmd;
204     if ( $file ) {
205         $cmd = $word;
206     } else {
207         $cmd = find_command($word);
208     }
209     unless ($cmd) {
210         printf STDERR "$word: No such command, or not unique.\n";
211         return (-1);
212     }
213
214     if ($cmd eq "help" || $cmd eq "exit" || $cmd eq "quit") {
215         return (&{$commands{$cmd}->{func}}(@arg));
216     }
217
218     # Call the function.
219     return (&{$commands{$cmd}->{func}}(@arg));
220 }
221
222 # set the object store in the ext2 formatted block device
223 sub Filesystem {
224     my $filesystem = shift;
225     $filesystem = "/dev/loop0" unless $filesystem;
226     
227     $::filesystem = $filesystem;
228     if (!defined($::st = stat($filesystem))) {
229         die "Unable to stat $filesystem.\n";
230     }
231 }
232
233 # select the OBD device we talk to
234 sub Device {
235     my $device = shift;
236
237     if (! $device ) {
238         $device = "/dev/obd0";
239     }
240     $::device = $device;
241     # Open the device, as we need an FD for the ioctl
242     sysopen(DEV_OBD, $device, 0) || die "Cannot open $device";
243     print "Device now $device\n";
244 }
245
246
247
248 sub Attach {
249     my $err = 0;
250     my $type = shift;
251     my $data;
252     my $datalen = 0;
253
254     if ($type eq "obdscsi" ) {
255         my $adapter = shift;
256         my $bus = shift;
257         my $tid = shift;
258         my $lun = shift;
259         $data = pack("iiiii", $adapter, $bus, $tid, $lun, $size);
260         $datalen = 4 * 4;
261     }
262
263     my $packed = pack("ipip", length($type), $type, $datalen, $data);
264
265     my $rc = ioctl(DEV_OBD, &OBD_IOC_ATTACH, $packed);
266
267     if (!defined $rc) {
268         print STDERR "ioctl failed: $!\n";
269     } elsif ($rc eq "0 but true") {
270         print "Finished (success)\n";
271     } else {
272         print "ioctl returned error code $rc.\n";
273     }
274 }
275
276 sub Detach {
277     my $err = 0;
278     my $data = "";
279     my $rc = ioctl(DEV_OBD, &OBD_IOC_DETACH, $data);
280
281     if (!defined $rc) {
282         print STDERR "ioctl failed: $!\n";
283     } elsif ($rc eq "0 but true") {
284         print "Finished (success)\n";
285     } else {
286         print "ioctl returned error code $rc.\n";
287     }
288 }
289
290
291 sub SnapAttach {
292     my $err = 0;
293     my $type = "snap_obd";
294     my $snapdev = shift;
295     my $snapno = shift;
296     my $file = shift;
297     my $snapcount;
298     my $table = {};
299     my $data;
300     my $datalen = 0;
301
302     if ( ! -f $file ) {
303         print "No such file $file\n";
304     }
305
306     $table = ReadSnapShotTable($file);
307
308     $snapcount = keys %{$table};
309     print "Snapcount $snapcount\n";
310
311     if ( ! defined $table->{0} ) {
312         print "No current snapshot in table! First make one\n";
313         return ;
314     }
315     $data = pack("iii", $snapdev, $snapno, $snapcount);
316     $datalen = 3 * 4;
317     foreach my $time (sort keys %{$table}) {
318         $data .= pack("Ii", $time, $table->{$time});
319         $datalen += 8;
320     }
321
322     my $len = length($type);
323     my $cl = length($data);
324     my $add = pack("p", $data);
325     print "type $type (len $len), datalen $datalen ($cl)\n";
326     my $packed = pack("ipip", length($type), $type, $datalen, $data);
327
328     my $rc = ioctl(DEV_OBD, &OBD_IOC_ATTACH, $packed);
329
330     if (!defined $rc) {
331         print STDERR "ioctl failed: $!\n";
332     } elsif ($rc eq "0 but true") {
333         print "Finished (success)\n";
334     } else {
335         print "ioctl returned error code $rc.\n";
336     }
337 }
338
339
340 sub SnapShotTable  {
341
342     my $file = &readl("enter file name: ");
343     if ( ! -f $file ) {
344         `touch $file`;
345     }
346     my $table = ReadSnapShotTable($file);
347   
348   again:
349     PrintSnapShotTable($table);
350     my $action = &readl("Add, Delete or Quit [adq]: ");
351     goto done if ($action  =~ "^q.*" );
352     goto add if ($action =~ "^a.*");
353     goto del  if ($action =~ "^d.*");
354     goto again;
355
356   add:
357     my $idx = &readl("enter index where you want this snapshot: ");
358     my $time = &readl("enter time or 'now' or 'current': ");
359     my $oldtime = SnapFindTimeFromIdx($idx, $table);
360     if (defined $oldtime) {
361         print "This already exists, first clean up\n";
362         goto again;
363     }
364
365     if ( $time  eq 'now' ) {
366         $time = time;
367     } elsif ( $time eq 'current' ) { 
368         $time = 0;
369     }
370     $table->{$time} = $idx;
371     goto again;
372
373   del:
374     $didx = &readl("Enter index to delete: ");
375     my $deltime = SnapFindTimeFromIdx($didx, $table);
376     delete $table->{$deltime} if defined $deltime;
377     goto again;
378
379   done:
380     my $ok = &readl("OK with new table? [Yn]: ");
381     unless ( $ok eq "no" )  {
382       WriteSnapShotTable($file, $table);
383   }
384 }
385
386 sub SnapFindTimeFromIdx {
387     my $idx = shift;
388     my $table = shift;
389
390     foreach my $time ( keys %{$table} ) {
391         if ( $table->{$time} == $idx ) {
392             return $time;
393         }
394     }
395     undef;
396 }
397
398 sub PrintSnapShotTable {
399     my $table = shift;
400     my $time;
401     
402     foreach  $time ( sort keys %{$table} ) {
403         my $stime = localtime($time);
404         if ( ! $time ) { 
405             $stime = "current";
406         }
407         printf "Time: %s -- Index %d\n", $stime, $table->{$time};
408     }
409 }
410
411 sub ReadSnapShotTable {
412
413     my $file = shift;
414     my $table = {};
415
416     open FH, "<$file";
417     while ( <FH> ) {
418         my ($time, $index) = split ;
419         $table->{$time} = $index;
420     }
421     close FH;
422
423     PrintSnapShotTable($table);
424
425     return $table;
426 }
427
428 sub WriteSnapShotTable {
429     my $file = shift;
430     my $table = shift;
431
432     open FH, ">$file";
433     foreach my $time ( sort keys %{$table}  ) {
434         print FH "$time $table->{$time}\n";
435     }
436     close FH;
437 }
438
439 sub Copy {
440     my $err = 0;
441     my $srcid = shift;
442     my $tgtid = shift;
443     my $data = pack("III", $::client_id, $srcid, $tgtid);
444     my $datalen = 12;
445
446     my $packed = pack("ip", $datalen, $data);
447     my $rc = ioctl(DEV_OBD, &OBD_IOC_COPY, $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 sub Migrate {
459     my $err = 0;
460     my $srcid = shift;
461     my $tgtid = shift;
462     my $data = pack("III", $::client_id, $srcid, $tgtid);
463     my $datalen = 12;
464
465     my $packed = pack("ip", $datalen, $data);
466     my $rc = ioctl(DEV_OBD, &OBD_IOC_MIGR, $packed);
467
468     if (!defined $rc) {
469         print STDERR "ioctl failed: $!\n";
470     } elsif ($rc eq "0 but true") {
471         print "Finished (success)\n";
472     } else {
473         print "ioctl returned error code $rc.\n";
474     }
475 }
476
477
478 sub Format {
479     my $err = 0;
480     my $size = shift;
481     my $data = pack("i", $size);
482     my $datalen = 4;
483
484     my $packed = pack("ip", $datalen, $data);
485     my $rc = ioctl(DEV_OBD, &OBD_IOC_FORMATOBD, $packed);
486
487     if (!defined $rc) {
488         print STDERR "ioctl failed: $!\n";
489     } elsif ($rc eq "0 but true") {
490         print "Finished (success)\n";
491     } else {
492         print "ioctl returned error code $rc.\n";
493     }
494 }
495
496 sub Partition {
497     my $err = 0;
498     my $partno = shift;
499     my $size = shift;
500     my $data = pack("ii", $partno, $size);
501     my $datalen = 2 * 4;
502
503     my $packed = pack("ip", $datalen, $data);
504     my $rc = ioctl(DEV_OBD, &OBD_IOC_PARTITION, $packed);
505
506     if (!defined $rc) {
507         print STDERR "ioctl failed: $!\n";
508     } elsif ($rc eq "0 but true") {
509         print "Finished (success)\n";
510     } else {
511         print "ioctl returned error code $rc.\n";
512     }
513 }
514
515 sub Setup {
516     my $err = 0;
517     my $type = shift;
518     my $data;
519     my $datalen = 0;
520     
521     $type = "ext2_obd" unless $type;
522
523     if ( $type eq "ext2_obd" ) {
524         my $dev = shift;
525         $dev = $::st->rdev() unless $dev;
526         $data = pack("i", $dev);
527         $datalen = 4;
528     }
529
530     my $packed = pack("ip", $datalen, $data);
531     my $rc = ioctl(DEV_OBD, &OBD_IOC_SETUP, $packed);
532
533     if (!defined $rc) {
534         print STDERR "ioctl failed: $!\n";
535     } elsif ($rc eq "0 but true") {
536         print "Finished (success)\n";
537     } else {
538         print "ioctl returned error code $rc.\n";
539     }
540 }
541
542 sub Cleanup {
543     my $err = "0";
544     my $rc = ioctl(DEV_OBD, &OBD_IOC_CLEANUP, $err);
545
546     if (!defined $rc) {
547         print STDERR "ioctl failed: $!\n";
548     } elsif ($rc eq "0 but true") {
549         print "Finished (success)\n";
550         $::client_id = 0;
551     } else {
552         print "ioctl returned error code $rc.\n";
553     }
554 }
555
556
557 sub Connect {
558     my $rc;
559
560     my $packed = "";
561     $rc = ioctl(DEV_OBD, &OBD_IOC_CONNECT, $packed);
562     $id = unpack("I", $packed);
563
564     if (!defined $rc) {
565         print STDERR "ioctl failed: $!\n";
566     } elsif ($rc eq "0 but true") {
567         $::client_id = $id;
568         print "Client ID     : $id\n";
569         print "Finished (success)\n";
570     } else {
571         print "ioctl returned error code $rc.\n";
572     }
573 }
574
575 sub Disconnect {
576     my $id = shift;
577
578     if (!defined($id)) {
579         $id = $::client_id;
580     }
581
582     if (!defined($id)) {
583         print "syntax: disconnect [client ID]\n";
584         print "When client ID is not given, the last valid client ID to be returned by a\n";
585         print "connect command this session is used; there is no such ID.\n";
586         return;
587     }
588
589     my $packed = pack("L", $id);
590     my $rc = ioctl(DEV_OBD, &OBD_IOC_DISCONNECT, $packed);
591
592     if (!defined $rc) {
593         print STDERR "ioctl failed: $!\n";
594     } elsif ($rc eq "0 but true") {
595         $::client_id = undef;
596         print "Finished (success)\n";
597     } else {
598         print "ioctl returned error code $rc.\n";
599     }
600 }
601
602 sub Create {
603     my $arg = shift;
604     my $quiet = shift;
605     my $rc;
606     my $prealloc = 0;
607
608     if (defined($quiet) && !($quiet eq "quiet")) {
609         print "syntax: create [number of objects [quiet]]\n";
610         return;
611     }
612
613     my $packed = pack("IL", $::client_id, $prealloc);
614     if (!defined($arg) || scalar($arg) < 2) {
615         print "Creating 1 object...\n";
616         $rc = ioctl(DEV_OBD, &OBD_IOC_CREATE, $packed);
617         if (!defined($quiet)) {
618             my $ino = unpack("L", $packed);
619             print "Created object #$ino.\n";
620         }
621     } else {
622         my $i;
623
624         print "Creating " . scalar($arg) . " objects...\n";
625         for ($i = 0; $i < scalar($arg); $i++) {
626             $rc = ioctl(DEV_OBD, &OBD_IOC_CREATE, $packed);
627             my $ino = unpack("L", $packed);
628             if (!($rc eq "0 but true")) {
629                 last;
630                 $packed = pack("IL", $::client_id, $prealloc);
631             } elsif (!defined($quiet)) {
632                 $packed = pack("IL", $::client_id, $prealloc);
633                 print "Created object #$ino.\n";
634             }
635         }
636     }
637
638     if (!defined $rc) {
639         print STDERR "ioctl failed: $!\n";
640     } elsif ($rc eq "0 but true") {
641         print "Finished (success)\n";
642     } else {
643         print "ioctl returned error code $rc.\n";
644     }
645 }
646
647 sub Sync {
648     my $err = "0";
649     my $rc = ioctl(DEV_OBD, &OBD_IOC_SYNC, $err);
650
651     if (!defined $rc) {
652         print STDERR "ioctl failed: $!\n";
653     } elsif ($rc eq "0 but true") {
654         print "Finished (success)\n";
655     } else {
656         print "ioctl returned error code $rc.\n";
657     }
658 }
659
660 sub Destroy {
661     if (!defined($::client_id)) {
662         print "You must first ``connect''.\n";
663         return;
664     }
665
666     my $arg = shift;
667
668     if (!defined($arg) || scalar($arg) < 1) {
669         print "destroy requires the object number to destroy.\n";
670         return;
671     }
672
673     print "Destroying object $arg...\n";
674     my $packed = pack("IL", $::client_id, $arg);
675     my $rc = ioctl(DEV_OBD, &OBD_IOC_DESTROY, $packed);
676
677     if (!defined $rc) {
678         print STDERR "ioctl failed: $!\n";
679     } elsif ($rc eq "0 but true") {
680         print "Finished (success)\n";
681     } else {
682         print "ioctl returned error code $rc.\n";
683     }
684 }
685
686 sub Getattr {
687     if (!defined($::client_id)) {
688         print "You must first ``connect''.\n";
689         return;
690     }
691
692     my $inode = shift;
693
694     if (!defined($inode) || scalar($inode) < 1) {
695         print "invalid arguments; type \"help getattr\" for a synopsis\n";
696         return;
697     }
698
699     # see Setattr
700     my $packed = pack("ILsx2lLLLI", $::client_id, $inode, 0, 0, 0, 0, 0, 0, 0,
701                       0);
702     my $rc = ioctl(DEV_OBD, &OBD_IOC_GETATTR, $packed);
703
704     if (!defined $rc) {
705         print STDERR "ioctl failed: $!\n";
706     } elsif ($rc eq "0 but true") {
707         my ($valid, $mode, $uid, $gid, $size, $atime, $mtime, $ctime, $flags);
708         ($valid, $mode, $uid, $gid, $size, $atime, $mtime, $ctime, $flags) =
709           unpack("ISssx2lLLLI", $packed);
710
711         printf("Inode: %d  Mode:  %o\n", $inode, $mode);
712         printf("User: %6d   Group: %6d   Size: %d\n", $uid, $gid, $size);
713         printf("ctime: %08lx -- %s\n", $ctime, scalar(gmtime($ctime)));
714         printf("atime: %08lx -- %s\n", $atime, scalar(gmtime($atime)));
715         printf("mtime: %08lx -- %s\n", $mtime, scalar(gmtime($mtime)));
716         printf("flags: %08x\n", $flags);
717         print "Finished (success)\n";
718     } else {
719         print "ioctl returned error code $rc.\n";
720     }
721 }
722
723 sub Setattr {
724     if (!defined($::client_id)) {
725         print "You must first ``connect''.\n";
726         return;
727     }
728
729     my $inode = shift;
730     my $valid = 0;
731     my $mode = oct(shift);
732     my $uid = shift;
733     my $gid = shift;
734     my $size = shift;
735     my $atime = shift;
736     my $mtime = shift;
737     my $ctime = shift;
738
739     if (defined($uid)) {
740         $valid |= &ATTR_UID;
741     }
742     if (defined($gid)) {
743         $valid |= &ATTR_GID;
744     }
745     if (defined($size)) {
746         $valid |= &ATTR_SIZE;
747     }
748     if (defined($atime)) {
749         $valid |= &ATTR_ATIME;
750     }
751     if (defined($mtime)) {
752         $valid |= &ATTR_MTIME;
753     }
754     if (defined($ctime)) {
755         $valid |= &ATTR_CTIME;
756     }
757     if (defined($mode)) {
758         $valid |= &ATTR_MODE;
759     }
760
761     if (!defined($inode) || scalar($inode) < 1) {
762         print "invalid arguments; type \"help setattr\" for a synopsis\n";
763         return;
764     }
765
766     #struct iattr {
767     #        unsigned int    ia_valid; (32)
768     #        umode_t         ia_mode; (16)
769     #        uid_t           ia_uid; (16)
770     #        gid_t           ia_gid; (16)
771     # -- 16 bit alignment here! --
772     #        off_t           ia_size; (32)
773     #        time_t          ia_atime; (32)
774     #        time_t          ia_mtime; (32)
775     #        time_t          ia_ctime; (32)
776     #        unsigned int    ia_attr_flags; (32)
777     #};
778
779     printf "valid is %x, mode is %o\n", $valid, $mode;
780     my $packed = pack("ILLSssx2ILLLL", $::client_id, $inode, $valid, $mode,
781                       $uid, $gid, $size, $atime, $mtime, $ctime, 0);
782     my $rc = ioctl(DEV_OBD, &OBD_IOC_SETATTR, $packed);
783
784     if (!defined $rc) {
785         print STDERR "ioctl failed: $!\n";
786     } elsif ($rc eq "0 but true") {
787         print "Finished (success)\n";
788     } else {
789         print "ioctl returned error code $rc.\n";
790     }
791 }
792
793 sub Read {
794     if (!defined($::client_id)) {
795         print "You must first ``connect''.\n";
796         return;
797     }
798
799     my $inode = shift;
800     my $count = shift;
801     my $offset = shift;
802   
803     if (!defined($inode) || scalar($inode) < 1 || !defined($count) ||
804         $count < 1 || (defined($offset) && $offset < 0)) {
805         print "invalid arguments; type \"help read\" for a synopsis\n";
806         return;
807     }
808
809     if (!defined($offset)) {
810         $offset = 0;
811     }
812
813     print("Reading $count bytes starting at byte $offset from object " .
814           "$inode...\n");
815
816     # "allocate" a large enough buffer
817     my $buf = sprintf("%${count}s", " ");
818     die "suck" if (length($buf) != $count);
819
820     # the perl we're using doesn't support pack type Q, and offset is 64 bits
821     my $packed = pack("ILpLLL", $::client_id, $inode, $buf, $count, $offset, 0);
822
823     my $rc = ioctl(DEV_OBD, &OBD_IOC_READ, $packed);
824
825     $retval = unpack("l", $packed);
826
827     if (!defined $rc) {
828         print STDERR "ioctl failed: $!\n";
829     } elsif ($rc eq "0 but true") {
830         if ($retval >= 0) {
831                 print substr($buf, 0, $retval);
832                 print "\nRead $retval of an attempted $count bytes.\n";
833                 print "Finished (success)\n";
834         } else {
835                 print "Finished (error $retval)\n";
836         }
837     } else {
838         print "ioctl returned error code $rc.\n";
839     }
840 }
841
842 sub Read2 {
843     if (!defined($::client_id)) {
844         print "You must first ``connect''.\n";
845         return;
846     }
847
848     my $inode = shift;
849     my $count = shift;
850     my $offset = shift;
851   
852     if (!defined($inode) || scalar($inode) < 1 || !defined($count) ||
853         $count < 1 || (defined($offset) && $offset < 0)) {
854         print "invalid arguments; type \"help read\" for a synopsis\n";
855         return;
856     }
857
858     if (!defined($offset)) {
859         $offset = 0;
860     }
861
862     print("Reading $count bytes starting at byte $offset from object " .
863           "$inode...\n");
864
865     # "allocate" a large enough buffer
866     my $buf = sprintf("%${count}s", " ");
867     die "suck" if (length($buf) != $count);
868
869     # the perl we're using doesn't support pack type Q, and offset is 64 bits
870     my $packed = pack("ILpLLL", $::client_id, $inode, $buf, $count, $offset, 0);
871
872     my $rc = ioctl(DEV_OBD, &OBD_IOC_READ2, $packed);
873
874     $retval = unpack("l", $packed);
875
876     if (!defined $rc) {
877         print STDERR "ioctl failed: $!\n";
878     } elsif ($rc eq "0 but true") {
879         if ($retval >= 0) {
880                 print substr($buf, 0, $retval);
881                 print "\nRead $retval of an attempted $count bytes.\n";
882                 print "Finished (success)\n";
883         } else {
884                 print "Finished (error $retval)\n";
885         }
886     } else {
887         print "ioctl returned error code $rc.\n";
888     }
889 }
890
891 sub Write {
892     if (!defined($::client_id)) {
893         print "You must first ``connect''.\n";
894         return;
895     }
896
897     my $inode = shift;
898     my $offset = shift;
899     my $text = join(' ', @_);
900     my $count = length($text);
901
902     if (!defined($inode) || scalar($inode) < 1 || !defined($offset) ||
903         scalar($offset) < 0) {
904         print "invalid arguments; type \"help write\" for a synopsis\n";
905         return;
906     }
907
908     if (!defined($text)) {
909         $text = "";
910         $count = 0;
911     }
912
913     print("Writing $count bytes starting at byte $offset to object " .
914           "$inode...\n");
915
916     # the perl we're using doesn't support pack type Q
917     my $packed = pack("ILpLLL", $::client_id, $inode, $text, $count, $offset, 0);
918     my $rc = ioctl(DEV_OBD, &OBD_IOC_WRITE, $packed);
919
920     $retval = unpack("l", $packed);
921
922     if (!defined $rc) {
923         print STDERR "ioctl failed: $!\n";
924     } elsif ($rc eq "0 but true") {
925         if ($retval >= 0) {
926                 print "\nWrote $retval of an attempted $count bytes.\n";
927                 print "Finished (success)\n";
928         } else {
929                 print "Finished (error $retval)\n";
930         }
931     } else {
932         print "ioctl returned error code $rc.\n";
933     }
934 }
935
936 sub Preallocate {
937     my $arg = shift;
938
939     if (!defined($::client_id)) {
940         print "You must first ``connect''.\n";
941         return;
942     }
943
944     if (!defined($arg) || scalar($arg) < 1 || scalar($arg) > 32) {
945         $arg = 32;
946     }
947
948     print "Preallocating $arg inodes...\n";
949     my $packed = pack("LLx128", $::client_id, $arg);
950     # client id, alloc, inodes[32]
951
952     my $rc = ioctl(DEV_OBD, &OBD_IOC_PREALLOCATE, $packed);
953
954     if (!defined $rc) {
955         print STDERR "ioctl failed: $!\n";
956     } elsif ($rc eq "0 but true") {
957         my $alloc = unpack("x4L", $packed);
958         my @inodes = unpack("x8L32", $packed);
959         my $i;
960
961         print "Got $alloc inodes: ";
962         foreach $i (@inodes) {
963             print $i . " ";
964         }
965         print "\nFinished (success)\n";
966     } else {
967         print "ioctl returned error code $rc.\n";
968     }
969 }
970
971 sub Decusecount {
972     my $rc = ioctl(DEV_OBD, &OBD_IOC_DEC_USE_COUNT, 0);
973
974     if (!defined $rc) {
975         print STDERR "ioctl failed: $!\n";
976     } elsif ($rc eq "0 but true") {
977         print "Finished (success)\n";
978     } else {
979         print "ioctl returned error code $rc.\n";
980     }
981 }
982
983 sub Statfs {
984     if (!defined($::client_id)) {
985         print "You must first ``connect''.\n";
986         return;
987     }
988
989     # struct statfs {
990     #         long f_type;
991     #         long f_bsize;
992     #         long f_blocks;
993     #         long f_bfree;
994     #         long f_bavail;
995     #         long f_files;
996     #         long f_ffree;
997     #         __kernel_fsid_t f_fsid; (64 bits)
998     #         long f_namelen;
999     #         long f_spare[6];
1000     # };
1001
1002     my $packed = pack("LLLLLLLIILL6", $::client_id, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1003                       0, 0, 0, 0, 0, 0);
1004
1005     my $rc = ioctl(DEV_OBD, &OBD_IOC_STATFS, $packed);
1006
1007     if (!defined $rc) {
1008         print STDERR "ioctl failed: $!\n";
1009     } elsif ($rc eq "0 but true") {
1010         # skip both the conn_id and the fs_type in the buffer
1011         my ($bsize, $blocks, $bfree, $bavail, $files, $ffree) =
1012             unpack("x4x4LLLLLL", $packed);
1013         print("$bsize byte blocks: $blocks, " . ($blocks - $bfree) . " used, " .
1014               "$bfree free ($bavail available).\n");
1015         print "$files files, " . ($files - $ffree) . " used, $ffree free.\n";
1016         print "Finished (success)\n";
1017     } else {
1018         print "ioctl returned error code $rc.\n";
1019     }
1020 }
1021
1022 sub Help {
1023     my $arg = shift;
1024
1025     if ( !$arg || !$commands{$arg} ) {
1026         print "Comands: ", join( ' ', @jcm_cmd_list), "\n";
1027     } else {
1028         print "Usage: " .  $commands{$arg}->{doc} . "\n";
1029     }
1030 }
1031
1032 sub Quit {
1033     if ($::client_id) {
1034         print "Disconnecting active session ($::client_id)...";
1035         Disconnect($::client_id);
1036     }
1037     exit;
1038 }