Whamcloud - gitweb
a399b7ee26e6b37915158a5567bc0ae1066815f8
[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     $data = pack("iii", $snapdev, $snapno, $snapcount);
311     $datalen = 3 * 4;
312     foreach my $time (sort keys %{$table}) {
313         $data .= pack("Ii", $time, $table->{$time});
314         $datalen += 8;
315     }
316
317     my $len = length($type);
318     my $cl = length($data);
319     my $add = pack("p", $data);
320     print "type $type (len $len), datalen $datalen ($cl)\n";
321     printf "Addr of data %x\n", $add; 
322     my $packed = pack("ipip", length($type), $type, $datalen, $data);
323
324     my $rc = ioctl(DEV_OBD, &OBD_IOC_ATTACH, $packed);
325
326     if (!defined $rc) {
327         print STDERR "ioctl failed: $!\n";
328     } elsif ($rc eq "0 but true") {
329         print "Finished (success)\n";
330     } else {
331         print "ioctl returned error code $rc.\n";
332     }
333 }
334
335
336 sub SnapShotTable  {
337
338     my $file = &readl("enter file name: ");
339     if ( ! -f $file ) {
340         `touch $file`;
341     }
342     my $table = ReadSnapShotTable($file);
343   
344   again:
345     PrintSnapShotTable($table);
346     my $action = &readl("Add, Delete or Quit [adq]: ");
347     goto done if ($action  =~ "^q.*" );
348     goto add if ($action =~ "^a.*");
349     goto del  if ($action =~ "^d.*");
350     goto again;
351
352   add:
353     my $idx = &readl("enter new index: ");
354     my $time = &readl("enter time (hit RET for \"now\"): ");
355     my $oldtime = SnapFindTimeFromIdx($idx, $table);
356     delete $table->{$oldtime} if defined $oldtime;
357     $time = time unless $time;
358     $table->{$time} = $idx;
359     goto again;
360
361   del:
362     $didx = &readl("Enter index to delete: ");
363     my $deltime = SnapFindTimeFromIdx($didx, $table);
364     delete $table->{$deltime} if defined $deltime;
365     goto again;
366
367   done:
368     my $ok = &readl("OK with new table? [Yn]: ");
369     unless ( $ok eq "no" )  {
370       WriteSnapShotTable($file, $table);
371   }
372 }
373
374 sub SnapFindTimeFromIdx {
375     my $idx = shift;
376     my $table = shift;
377
378     foreach my $time ( keys %{$table} ) {
379         if ( $table->{$time} == $idx ) {
380             return $time;
381         }
382     }
383     undef;
384 }
385
386 sub PrintSnapShotTable {
387     my $table = shift;
388     my $time;
389     
390     foreach  $time ( sort keys %{$table} ) {
391         my $stime = localtime($time);
392         printf "Time: %s -- Index %d\n", $stime, $table->{$time};
393     }
394 }
395
396 sub ReadSnapShotTable {
397
398     my $file = shift;
399     my $table = {};
400
401     open FH, "<$file";
402     while ( <FH> ) {
403         my ($time, $index) = split ;
404         $table->{$time} = $index;
405     }
406     close FH;
407     return $table;
408 }
409
410 sub WriteSnapShotTable {
411     my $file = shift;
412     my $table = shift;
413
414     open FH, ">$file";
415     foreach my $time ( sort keys %{$table}  ) {
416         print FH "$time $table->{$time}\n";
417     }
418     close FH;
419 }
420
421 sub Copy {
422     my $err = 0;
423     my $srcid = shift;
424     my $tgtid = shift;
425     my $data = pack("III", $::client_id, $srcid, $tgtid);
426     my $datalen = 12;
427
428     my $packed = pack("ip", $datalen, $data);
429     my $rc = ioctl(DEV_OBD, &OBD_IOC_COPY, $packed);
430
431     if (!defined $rc) {
432         print STDERR "ioctl failed: $!\n";
433     } elsif ($rc eq "0 but true") {
434         print "Finished (success)\n";
435     } else {
436         print "ioctl returned error code $rc.\n";
437     }
438 }
439
440 sub Migrate {
441     my $err = 0;
442     my $srcid = shift;
443     my $tgtid = shift;
444     my $data = pack("III", $::client_id, $srcid, $tgtid);
445     my $datalen = 12;
446
447     my $packed = pack("ip", $datalen, $data);
448     my $rc = ioctl(DEV_OBD, &OBD_IOC_MIGR, $packed);
449
450     if (!defined $rc) {
451         print STDERR "ioctl failed: $!\n";
452     } elsif ($rc eq "0 but true") {
453         print "Finished (success)\n";
454     } else {
455         print "ioctl returned error code $rc.\n";
456     }
457 }
458
459
460 sub Format {
461     my $err = 0;
462     my $size = shift;
463     my $data = pack("i", $size);
464     my $datalen = 4;
465
466     my $packed = pack("ip", $datalen, $data);
467     my $rc = ioctl(DEV_OBD, &OBD_IOC_FORMATOBD, $packed);
468
469     if (!defined $rc) {
470         print STDERR "ioctl failed: $!\n";
471     } elsif ($rc eq "0 but true") {
472         print "Finished (success)\n";
473     } else {
474         print "ioctl returned error code $rc.\n";
475     }
476 }
477
478 sub Partition {
479     my $err = 0;
480     my $partno = shift;
481     my $size = shift;
482     my $data = pack("ii", $partno, $size);
483     my $datalen = 2 * 4;
484
485     my $packed = pack("ip", $datalen, $data);
486     my $rc = ioctl(DEV_OBD, &OBD_IOC_PARTITION, $packed);
487
488     if (!defined $rc) {
489         print STDERR "ioctl failed: $!\n";
490     } elsif ($rc eq "0 but true") {
491         print "Finished (success)\n";
492     } else {
493         print "ioctl returned error code $rc.\n";
494     }
495 }
496
497 sub Setup {
498     my $err = 0;
499     my $type = shift;
500     my $data;
501     my $datalen = 0;
502     
503     $type = "ext2_obd" unless $type;
504
505     if ( $type eq "ext2_obd" ) {
506         my $dev = shift;
507         $dev = $::st->rdev() unless $dev;
508         $data = pack("i", $dev);
509         $datalen = 4;
510     }
511
512     my $packed = pack("ip", $datalen, $data);
513     my $rc = ioctl(DEV_OBD, &OBD_IOC_SETUP, $packed);
514
515     if (!defined $rc) {
516         print STDERR "ioctl failed: $!\n";
517     } elsif ($rc eq "0 but true") {
518         print "Finished (success)\n";
519     } else {
520         print "ioctl returned error code $rc.\n";
521     }
522 }
523
524 sub Cleanup {
525     my $err = "0";
526     my $rc = ioctl(DEV_OBD, &OBD_IOC_CLEANUP, $err);
527
528     if (!defined $rc) {
529         print STDERR "ioctl failed: $!\n";
530     } elsif ($rc eq "0 but true") {
531         print "Finished (success)\n";
532         $::client_id = 0;
533     } else {
534         print "ioctl returned error code $rc.\n";
535     }
536 }
537
538
539 sub Connect {
540     my $rc;
541
542     my $packed = "";
543     $rc = ioctl(DEV_OBD, &OBD_IOC_CONNECT, $packed);
544     $id = unpack("I", $packed);
545
546     if (!defined $rc) {
547         print STDERR "ioctl failed: $!\n";
548     } elsif ($rc eq "0 but true") {
549         $::client_id = $id;
550         print "Client ID     : $id\n";
551         print "Finished (success)\n";
552     } else {
553         print "ioctl returned error code $rc.\n";
554     }
555 }
556
557 sub Disconnect {
558     my $id = shift;
559
560     if (!defined($id)) {
561         $id = $::client_id;
562     }
563
564     if (!defined($id)) {
565         print "syntax: disconnect [client ID]\n";
566         print "When client ID is not given, the last valid client ID to be returned by a\n";
567         print "connect command this session is used; there is no such ID.\n";
568         return;
569     }
570
571     my $packed = pack("L", $id);
572     my $rc = ioctl(DEV_OBD, &OBD_IOC_DISCONNECT, $packed);
573
574     if (!defined $rc) {
575         print STDERR "ioctl failed: $!\n";
576     } elsif ($rc eq "0 but true") {
577         $::client_id = undef;
578         print "Finished (success)\n";
579     } else {
580         print "ioctl returned error code $rc.\n";
581     }
582 }
583
584 sub Create {
585     my $arg = shift;
586     my $quiet = shift;
587     my $rc;
588     my $prealloc = 0;
589
590     if (defined($quiet) && !($quiet eq "quiet")) {
591         print "syntax: create [number of objects [quiet]]\n";
592         return;
593     }
594
595     my $packed = pack("IL", $::client_id, $prealloc);
596     if (!defined($arg) || scalar($arg) < 2) {
597         print "Creating 1 object...\n";
598         $rc = ioctl(DEV_OBD, &OBD_IOC_CREATE, $packed);
599         if (!defined($quiet)) {
600             my $ino = unpack("L", $packed);
601             print "Created object #$ino.\n";
602         }
603     } else {
604         my $i;
605
606         print "Creating " . scalar($arg) . " objects...\n";
607         for ($i = 0; $i < scalar($arg); $i++) {
608             $rc = ioctl(DEV_OBD, &OBD_IOC_CREATE, $packed);
609             my $ino = unpack("L", $packed);
610             if (!($rc eq "0 but true")) {
611                 last;
612                 $packed = pack("IL", $::client_id, $prealloc);
613             } elsif (!defined($quiet)) {
614                 $packed = pack("IL", $::client_id, $prealloc);
615                 print "Created object #$ino.\n";
616             }
617         }
618     }
619
620     if (!defined $rc) {
621         print STDERR "ioctl failed: $!\n";
622     } elsif ($rc eq "0 but true") {
623         print "Finished (success)\n";
624     } else {
625         print "ioctl returned error code $rc.\n";
626     }
627 }
628
629 sub Sync {
630     my $err = "0";
631     my $rc = ioctl(DEV_OBD, &OBD_IOC_SYNC, $err);
632
633     if (!defined $rc) {
634         print STDERR "ioctl failed: $!\n";
635     } elsif ($rc eq "0 but true") {
636         print "Finished (success)\n";
637     } else {
638         print "ioctl returned error code $rc.\n";
639     }
640 }
641
642 sub Destroy {
643     if (!defined($::client_id)) {
644         print "You must first ``connect''.\n";
645         return;
646     }
647
648     my $arg = shift;
649
650     if (!defined($arg) || scalar($arg) < 1) {
651         print "destroy requires the object number to destroy.\n";
652         return;
653     }
654
655     print "Destroying object $arg...\n";
656     my $packed = pack("IL", $::client_id, $arg);
657     my $rc = ioctl(DEV_OBD, &OBD_IOC_DESTROY, $packed);
658
659     if (!defined $rc) {
660         print STDERR "ioctl failed: $!\n";
661     } elsif ($rc eq "0 but true") {
662         print "Finished (success)\n";
663     } else {
664         print "ioctl returned error code $rc.\n";
665     }
666 }
667
668 sub Getattr {
669     if (!defined($::client_id)) {
670         print "You must first ``connect''.\n";
671         return;
672     }
673
674     my $inode = shift;
675
676     if (!defined($inode) || scalar($inode) < 1) {
677         print "invalid arguments; type \"help getattr\" for a synopsis\n";
678         return;
679     }
680
681     # see Setattr
682     my $packed = pack("ILsx2lLLLI", $::client_id, $inode, 0, 0, 0, 0, 0, 0, 0,
683                       0);
684     my $rc = ioctl(DEV_OBD, &OBD_IOC_GETATTR, $packed);
685
686     if (!defined $rc) {
687         print STDERR "ioctl failed: $!\n";
688     } elsif ($rc eq "0 but true") {
689         my ($valid, $mode, $uid, $gid, $size, $atime, $mtime, $ctime, $flags);
690         ($valid, $mode, $uid, $gid, $size, $atime, $mtime, $ctime, $flags) =
691           unpack("ISssx2lLLLI", $packed);
692
693         printf("Inode: %d  Mode:  %o\n", $inode, $mode);
694         printf("User: %6d   Group: %6d   Size: %d\n", $uid, $gid, $size);
695         printf("ctime: %08lx -- %s\n", $ctime, scalar(gmtime($ctime)));
696         printf("atime: %08lx -- %s\n", $atime, scalar(gmtime($atime)));
697         printf("mtime: %08lx -- %s\n", $mtime, scalar(gmtime($mtime)));
698         printf("flags: %08x\n", $flags);
699         print "Finished (success)\n";
700     } else {
701         print "ioctl returned error code $rc.\n";
702     }
703 }
704
705 sub Setattr {
706     if (!defined($::client_id)) {
707         print "You must first ``connect''.\n";
708         return;
709     }
710
711     my $inode = shift;
712     my $valid = 0;
713     my $mode = oct(shift);
714     my $uid = shift;
715     my $gid = shift;
716     my $size = shift;
717     my $atime = shift;
718     my $mtime = shift;
719     my $ctime = shift;
720
721     if (defined($uid)) {
722         $valid |= &ATTR_UID;
723     }
724     if (defined($gid)) {
725         $valid |= &ATTR_GID;
726     }
727     if (defined($size)) {
728         $valid |= &ATTR_SIZE;
729     }
730     if (defined($atime)) {
731         $valid |= &ATTR_ATIME;
732     }
733     if (defined($mtime)) {
734         $valid |= &ATTR_MTIME;
735     }
736     if (defined($ctime)) {
737         $valid |= &ATTR_CTIME;
738     }
739     if (defined($mode)) {
740         $valid |= &ATTR_MODE;
741     }
742
743     if (!defined($inode) || scalar($inode) < 1) {
744         print "invalid arguments; type \"help setattr\" for a synopsis\n";
745         return;
746     }
747
748     #struct iattr {
749     #        unsigned int    ia_valid; (32)
750     #        umode_t         ia_mode; (16)
751     #        uid_t           ia_uid; (16)
752     #        gid_t           ia_gid; (16)
753     # -- 16 bit alignment here! --
754     #        off_t           ia_size; (32)
755     #        time_t          ia_atime; (32)
756     #        time_t          ia_mtime; (32)
757     #        time_t          ia_ctime; (32)
758     #        unsigned int    ia_attr_flags; (32)
759     #};
760
761     printf "valid is %x, mode is %o\n", $valid, $mode;
762     my $packed = pack("ILLSssx2ILLLL", $::client_id, $inode, $valid, $mode,
763                       $uid, $gid, $size, $atime, $mtime, $ctime, 0);
764     my $rc = ioctl(DEV_OBD, &OBD_IOC_SETATTR, $packed);
765
766     if (!defined $rc) {
767         print STDERR "ioctl failed: $!\n";
768     } elsif ($rc eq "0 but true") {
769         print "Finished (success)\n";
770     } else {
771         print "ioctl returned error code $rc.\n";
772     }
773 }
774
775 sub Read {
776     if (!defined($::client_id)) {
777         print "You must first ``connect''.\n";
778         return;
779     }
780
781     my $inode = shift;
782     my $count = shift;
783     my $offset = shift;
784   
785     if (!defined($inode) || scalar($inode) < 1 || !defined($count) ||
786         $count < 1 || (defined($offset) && $offset < 0)) {
787         print "invalid arguments; type \"help read\" for a synopsis\n";
788         return;
789     }
790
791     if (!defined($offset)) {
792         $offset = 0;
793     }
794
795     print("Reading $count bytes starting at byte $offset from object " .
796           "$inode...\n");
797
798     # "allocate" a large enough buffer
799     my $buf = sprintf("%${count}s", " ");
800     die "suck" if (length($buf) != $count);
801
802     # the perl we're using doesn't support pack type Q, and offset is 64 bits
803     my $packed = pack("ILpLLL", $::client_id, $inode, $buf, $count, $offset, 0);
804
805     my $rc = ioctl(DEV_OBD, &OBD_IOC_READ, $packed);
806
807     $retval = unpack("l", $packed);
808
809     if (!defined $rc) {
810         print STDERR "ioctl failed: $!\n";
811     } elsif ($rc eq "0 but true") {
812         if ($retval >= 0) {
813                 print substr($buf, 0, $retval);
814                 print "\nRead $retval of an attempted $count bytes.\n";
815                 print "Finished (success)\n";
816         } else {
817                 print "Finished (error $retval)\n";
818         }
819     } else {
820         print "ioctl returned error code $rc.\n";
821     }
822 }
823
824 sub Read2 {
825     if (!defined($::client_id)) {
826         print "You must first ``connect''.\n";
827         return;
828     }
829
830     my $inode = shift;
831     my $count = shift;
832     my $offset = shift;
833   
834     if (!defined($inode) || scalar($inode) < 1 || !defined($count) ||
835         $count < 1 || (defined($offset) && $offset < 0)) {
836         print "invalid arguments; type \"help read\" for a synopsis\n";
837         return;
838     }
839
840     if (!defined($offset)) {
841         $offset = 0;
842     }
843
844     print("Reading $count bytes starting at byte $offset from object " .
845           "$inode...\n");
846
847     # "allocate" a large enough buffer
848     my $buf = sprintf("%${count}s", " ");
849     die "suck" if (length($buf) != $count);
850
851     # the perl we're using doesn't support pack type Q, and offset is 64 bits
852     my $packed = pack("ILpLLL", $::client_id, $inode, $buf, $count, $offset, 0);
853
854     my $rc = ioctl(DEV_OBD, &OBD_IOC_READ2, $packed);
855
856     $retval = unpack("l", $packed);
857
858     if (!defined $rc) {
859         print STDERR "ioctl failed: $!\n";
860     } elsif ($rc eq "0 but true") {
861         if ($retval >= 0) {
862                 print substr($buf, 0, $retval);
863                 print "\nRead $retval of an attempted $count bytes.\n";
864                 print "Finished (success)\n";
865         } else {
866                 print "Finished (error $retval)\n";
867         }
868     } else {
869         print "ioctl returned error code $rc.\n";
870     }
871 }
872
873 sub Write {
874     if (!defined($::client_id)) {
875         print "You must first ``connect''.\n";
876         return;
877     }
878
879     my $inode = shift;
880     my $offset = shift;
881     my $text = join(' ', @_);
882     my $count = length($text);
883
884     if (!defined($inode) || scalar($inode) < 1 || !defined($offset) ||
885         scalar($offset) < 0) {
886         print "invalid arguments; type \"help write\" for a synopsis\n";
887         return;
888     }
889
890     if (!defined($text)) {
891         $text = "";
892         $count = 0;
893     }
894
895     print("Writing $count bytes starting at byte $offset to object " .
896           "$inode...\n");
897
898     # the perl we're using doesn't support pack type Q
899     my $packed = pack("ILpLLL", $::client_id, $inode, $text, $count, $offset, 0);
900     my $rc = ioctl(DEV_OBD, &OBD_IOC_WRITE, $packed);
901
902     $retval = unpack("l", $packed);
903
904     if (!defined $rc) {
905         print STDERR "ioctl failed: $!\n";
906     } elsif ($rc eq "0 but true") {
907         if ($retval >= 0) {
908                 print "\nWrote $retval of an attempted $count bytes.\n";
909                 print "Finished (success)\n";
910         } else {
911                 print "Finished (error $retval)\n";
912         }
913     } else {
914         print "ioctl returned error code $rc.\n";
915     }
916 }
917
918 sub Preallocate {
919     my $arg = shift;
920
921     if (!defined($::client_id)) {
922         print "You must first ``connect''.\n";
923         return;
924     }
925
926     if (!defined($arg) || scalar($arg) < 1 || scalar($arg) > 32) {
927         $arg = 32;
928     }
929
930     print "Preallocating $arg inodes...\n";
931     my $packed = pack("LLx128", $::client_id, $arg);
932     # client id, alloc, inodes[32]
933
934     my $rc = ioctl(DEV_OBD, &OBD_IOC_PREALLOCATE, $packed);
935
936     if (!defined $rc) {
937         print STDERR "ioctl failed: $!\n";
938     } elsif ($rc eq "0 but true") {
939         my $alloc = unpack("x4L", $packed);
940         my @inodes = unpack("x8L32", $packed);
941         my $i;
942
943         print "Got $alloc inodes: ";
944         foreach $i (@inodes) {
945             print $i . " ";
946         }
947         print "\nFinished (success)\n";
948     } else {
949         print "ioctl returned error code $rc.\n";
950     }
951 }
952
953 sub Decusecount {
954     my $rc = ioctl(DEV_OBD, &OBD_IOC_DEC_USE_COUNT, 0);
955
956     if (!defined $rc) {
957         print STDERR "ioctl failed: $!\n";
958     } elsif ($rc eq "0 but true") {
959         print "Finished (success)\n";
960     } else {
961         print "ioctl returned error code $rc.\n";
962     }
963 }
964
965 sub Statfs {
966     if (!defined($::client_id)) {
967         print "You must first ``connect''.\n";
968         return;
969     }
970
971     # struct statfs {
972     #         long f_type;
973     #         long f_bsize;
974     #         long f_blocks;
975     #         long f_bfree;
976     #         long f_bavail;
977     #         long f_files;
978     #         long f_ffree;
979     #         __kernel_fsid_t f_fsid; (64 bits)
980     #         long f_namelen;
981     #         long f_spare[6];
982     # };
983
984     my $packed = pack("LLLLLLLIILL6", $::client_id, 0, 0, 0, 0, 0, 0, 0, 0, 0,
985                       0, 0, 0, 0, 0, 0);
986
987     my $rc = ioctl(DEV_OBD, &OBD_IOC_STATFS, $packed);
988
989     if (!defined $rc) {
990         print STDERR "ioctl failed: $!\n";
991     } elsif ($rc eq "0 but true") {
992         # skip both the conn_id and the fs_type in the buffer
993         my ($bsize, $blocks, $bfree, $bavail, $files, $ffree) =
994             unpack("x4x4LLLLLL", $packed);
995         print("$bsize byte blocks: $blocks, " . ($blocks - $bfree) . " used, " .
996               "$bfree free ($bavail available).\n");
997         print "$files files, " . ($files - $ffree) . " used, $ffree free.\n";
998         print "Finished (success)\n";
999     } else {
1000         print "ioctl returned error code $rc.\n";
1001     }
1002 }
1003
1004 sub Help {
1005     my $arg = shift;
1006
1007     if ( !$arg || !$commands{$arg} ) {
1008         print "Comands: ", join( ' ', @jcm_cmd_list), "\n";
1009     } else {
1010         print "Usage: " .  $commands{$arg}->{doc} . "\n";
1011     }
1012 }
1013
1014 sub Quit {
1015     if ($::client_id) {
1016         print "Disconnecting active session ($::client_id)...";
1017         Disconnect($::client_id);
1018     }
1019     exit;
1020 }