Whamcloud - gitweb
New direcotory layout:
[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_OBDDEV () { &_IOC(1, ord(\'f\'), 4, 4);}' unless
25   defined(&OBD_IOC_SETUP_OBDDEV);
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
59 eval 'sub ATTR_MODE () {1;}' unless defined(&ATTR_MODE);
60 eval 'sub ATTR_UID () {2;}' unless defined(&ATTR_UID);
61 eval 'sub ATTR_GID () {4;}' unless defined(&ATTR_GID);
62 eval 'sub ATTR_SIZE () {8;}' unless defined(&ATTR_SIZE);
63 eval 'sub ATTR_ATIME () {16;}' unless defined(&ATTR_ATIME);
64 eval 'sub ATTR_MTIME () {32;}' unless defined(&ATTR_MTIME);
65 eval 'sub ATTR_CTIME () {64;}' unless defined(&ATTR_CTIME);
66
67 use Getopt::Long;
68 use File::stat;
69 use Storable;
70 use Carp;
71 use Term::ReadLine;
72 use IO::Handle;
73
74
75 my ($device, $filesystem, $file);
76 # startup options (I'll replace these when I have some to replace with)
77 GetOptions("f!" => \$file, "device=s" => \$device, "fs=s" => $filesystem) || die "Getoptions";
78
79 # genuine new simulated OBD device
80 $device = "/dev/obd0" unless $device;
81 # object store in the ext2 formatted block device
82 $filesystem = "/dev/loop0" unless $filesystem;
83
84
85
86 # get a console for the app
87
88 my $line;
89 my $command;
90 my $arg;
91
92 my %commands =
93     ('create' => {func => "Create", doc => "create: creates a new inode"},
94      'attach' => {func => "Attach", doc => "format type [adapter bus tid lun]"},
95      'format' => {func => "Format", doc => "format type adapter bus tid lun size"},
96      'partition' => {func => "Partition", doc => "partition type adapter bus tid lun partition size"},
97      'setup' => {func => "Setup", doc => "setup: link the ext2 partition (default /dev/loop0) to this obddev"},
98      'connect' => {func => "Connect", doc => "connect: allocates client ID for this session"},
99      'disconnect' => {func => "Disconnect", doc => "disconnect [id]: frees client resources"},
100      'sync' => {func => "Sync", doc => "sync: flushes buffers to disk"},
101      'destroy' => {func => "Destroy", doc => "setup: destroys an inode"},
102      'cleanup' => {func => "Cleanup", doc => "cleanup the minor obd device"},
103      'dec_use_count' => {func => "Decusecount", doc => "decreases the module use count so that the module can be removed following an oops"},
104      'read' => {func => "Read", doc => "read <inode> <count> [offset]"},
105      'fsread' => {func => "Read2", doc => "read <inode> <count> [offset]"},
106      'write' => {func => "Write", doc => "write <inode> <offset> <text>"},
107      'setattr' => {func => "Setattr", doc => "setattr <inode> [mode [uid [gid [size [atime [mtime [ctime]]]]]]]"},
108      'getattr' => {func => "Getattr", doc => "getattr <inode>: displays inode object attributes"},
109      'preallocate' => {func => "Preallocate", doc => "preallocate [num]: requests preallocation of num inodes."},
110      'statfs' => {func => "Statfs", doc => "statfs: filesystem status information"},
111      'help' => {func => \&Help,  doc => "help: this message"},
112      'quit' => {func => \&Quit,  doc => "see \"exit\""},
113      'exit' => {func => \&Quit,  doc => "see \"quit\""}
114     );
115
116 #
117 #       setup completion function
118 #
119 my @jcm_cmd_list = keys %commands;
120
121 #------------------------------------------------------------------------------
122 # Open the device, as we need an FD for the ioctl
123 sysopen(DEV_OBD, $device, 0) || die "Cannot open $device";
124
125 if (!defined($::st = stat($filesystem))) {
126     die "Unable to stat $filesystem.\n";
127 }
128
129 my $term, $attribs;
130
131 if ( $file ) {
132         while ( <STDIN> ) {
133             print $_;
134             execute_line($_);
135         }
136         exit 0;
137 } else {
138     $term = new Term::ReadLine 'obdcontrol ';
139     $attribs = $term->Attribs;
140     $attribs->{attempted_completion_function} = \&completeme;
141     $term->ornaments('md,me,,');        # bold face prompt
142     
143     # make sure stdout is not buffered
144     STDOUT->autoflush(1);
145
146     # Get on with the show
147     process_line();
148 }
149
150 #------------------------------------------------------------------------------
151 sub completeme {
152     my ($text, $line, $start, $end) = @_;
153     if (substr($line, 0, $start) =~ /^\s*$/) {
154         $attribs->{completion_word} = \@jcm_cmd_list;
155         return $term->completion_matches($text,
156                                          $attribs->{'list_completion_function'});
157     }
158 }
159
160 sub find_command {
161     my $given = shift;
162     my $name;
163     my @completions = completeme($given, $given, 0, length($given));
164     if ($#completions == 0) {
165         $name = shift @completions;
166     }
167
168     return $name;
169 }
170
171 # start making requests
172 sub process_line {
173   foo:
174     $line = $term->readline("obdcontrol > ");
175     execute_line($line);
176     goto foo;
177 }
178
179 sub execute_line {
180     my $line = shift;
181
182     my @arg = split(' ', $line);
183     my $word = shift @arg;
184
185     my $cmd;
186     if ( $file ) {
187         $cmd = $word;
188     } else {
189         $cmd = find_command($word);
190     }
191     unless ($cmd) {
192         printf STDERR "$word: No such command, or not unique.\n";
193         return (-1);
194     }
195
196     if ($cmd eq "help" || $cmd eq "exit" || $cmd eq "quit") {
197         return (&{$commands{$cmd}->{func}}(@arg));
198     }
199
200     # Call the function.
201     return (&{$commands{$cmd}->{func}}(@arg));
202 }
203
204
205 sub Attach {
206     my $err = 0;
207     my $type = shift;
208     my $data;
209     my $datalen = 0;
210
211     if ($type eq "obdscsi" ) {
212         my $adapter = shift;
213         my $bus = shift;
214         my $tid = shift;
215         my $lun = shift;
216         $data = pack("iiiii", $adapter, $bus, $tid, $lun, $size);
217         $datalen = 4 * 4;
218     }
219
220     my $packed = pack("ipip", length($type), $type, $datalen, $data);
221
222     my $rc = ioctl(DEV_OBD, &OBD_IOC_ATTACH, $packed);
223
224     if (!defined $rc) {
225         print STDERR "ioctl failed: $!\n";
226     } elsif ($rc eq "0 but true") {
227         print "Finished (success)\n";
228     } else {
229         print "ioctl returned error code $rc.\n";
230     }
231 }
232
233 sub Format {
234     my $err = 0;
235     my $size = shift;
236     my $data = pack("i", $size);
237     my $datalen = 4;
238
239     my $packed = pack("ip", $datalen, $data);
240     my $rc = ioctl(DEV_OBD, &OBD_IOC_FORMATOBD, $packed);
241
242     if (!defined $rc) {
243         print STDERR "ioctl failed: $!\n";
244     } elsif ($rc eq "0 but true") {
245         print "Finished (success)\n";
246     } else {
247         print "ioctl returned error code $rc.\n";
248     }
249 }
250
251 sub Partition {
252     my $err = 0;
253     my $partno = shift;
254     my $size = shift;
255     my $data = pack("ii", $partno, $size);
256     my $datalen = 2 * 4;
257
258     my $packed = pack("ip", $datalen, $data);
259     my $rc = ioctl(DEV_OBD, &OBD_IOC_PARTITION, $packed);
260
261     if (!defined $rc) {
262         print STDERR "ioctl failed: $!\n";
263     } elsif ($rc eq "0 but true") {
264         print "Finished (success)\n";
265     } else {
266         print "ioctl returned error code $rc.\n";
267     }
268 }
269
270 sub Setup {
271     my $err = 0;
272     my $type = shift;
273     my $data;
274     my $datalen = 0;
275     
276     $type = "sim_obd" unless $type;
277
278     if ( $type eq "sim_obd" ) {
279         my $dev = shift;
280         $dev = $::st->rdev() unless $dev;
281         $data = pack("i", $dev);
282         $datalen = 4;
283     }
284
285     my $packed = pack("ip", $datalen, $data);
286     my $rc = ioctl(DEV_OBD, &OBD_IOC_SETUP_OBDDEV, $packed);
287
288     if (!defined $rc) {
289         print STDERR "ioctl failed: $!\n";
290     } elsif ($rc eq "0 but true") {
291         print "Finished (success)\n";
292     } else {
293         print "ioctl returned error code $rc.\n";
294     }
295 }
296
297 sub Cleanup {
298     my $err = "0";
299     my $rc = ioctl(DEV_OBD, &OBD_IOC_CLEANUP, $err);
300
301     if (!defined $rc) {
302         print STDERR "ioctl failed: $!\n";
303     } elsif ($rc eq "0 but true") {
304         print "Finished (success)\n";
305         $::client_id = 0;
306     } else {
307         print "ioctl returned error code $rc.\n";
308     }
309 }
310
311
312 sub Connect {
313     my $rc;
314
315     my $packed = "";
316     $rc = ioctl(DEV_OBD, &OBD_IOC_CONNECT, $packed);
317     $id = unpack("I", $packed);
318
319     if (!defined $rc) {
320         print STDERR "ioctl failed: $!\n";
321     } elsif ($rc eq "0 but true") {
322         $::client_id = $id;
323         print "Client ID     : $id\n";
324         print "Finished (success)\n";
325     } else {
326         print "ioctl returned error code $rc.\n";
327     }
328 }
329
330 sub Disconnect {
331     my $id = shift;
332
333     if (!defined($id)) {
334         $id = $::client_id;
335     }
336
337     if (!defined($id)) {
338         print "syntax: disconnect [client ID]\n";
339         print "When client ID is not given, the last valid client ID to be returned by a\n";
340         print "connect command this session is used; there is no such ID.\n";
341         return;
342     }
343
344     my $packed = pack("L", $id);
345     my $rc = ioctl(DEV_OBD, &OBD_IOC_DISCONNECT, $packed);
346
347     if (!defined $rc) {
348         print STDERR "ioctl failed: $!\n";
349     } elsif ($rc eq "0 but true") {
350         $::client_id = undef;
351         print "Finished (success)\n";
352     } else {
353         print "ioctl returned error code $rc.\n";
354     }
355 }
356
357 sub Create {
358     my $arg = shift;
359     my $quiet = shift;
360     my $rc;
361     my $prealloc = 0;
362
363     if (defined($quiet) && !($quiet eq "quiet")) {
364         print "syntax: create [number of objects [quiet]]\n";
365         return;
366     }
367
368     my $packed = pack("IL", $::client_id, $prealloc);
369     if (!defined($arg) || scalar($arg) < 2) {
370         print "Creating 1 object...\n";
371         $rc = ioctl(DEV_OBD, &OBD_IOC_CREATE, $packed);
372         if (!defined($quiet)) {
373             my $ino = unpack("L", $packed);
374             print "Created object #$ino.\n";
375         }
376     } else {
377         my $i;
378
379         print "Creating " . scalar($arg) . " objects...\n";
380         for ($i = 0; $i < scalar($arg); $i++) {
381             $rc = ioctl(DEV_OBD, &OBD_IOC_CREATE, $packed);
382             my $ino = unpack("L", $packed);
383             if (!($rc eq "0 but true")) {
384                 last;
385                 $packed = pack("IL", $::client_id, $prealloc);
386             } elsif (!defined($quiet)) {
387                 $packed = pack("IL", $::client_id, $prealloc);
388                 print "Created object #$ino.\n";
389             }
390         }
391     }
392
393     if (!defined $rc) {
394         print STDERR "ioctl failed: $!\n";
395     } elsif ($rc eq "0 but true") {
396         print "Finished (success)\n";
397     } else {
398         print "ioctl returned error code $rc.\n";
399     }
400 }
401
402 sub Sync {
403     my $err = "0";
404     my $rc = ioctl(DEV_OBD, &OBD_IOC_SYNC, $err);
405
406     if (!defined $rc) {
407         print STDERR "ioctl failed: $!\n";
408     } elsif ($rc eq "0 but true") {
409         print "Finished (success)\n";
410     } else {
411         print "ioctl returned error code $rc.\n";
412     }
413 }
414
415 sub Destroy {
416     if (!defined($::client_id)) {
417         print "You must first ``connect''.\n";
418         return;
419     }
420
421     my $arg = shift;
422
423     if (!defined($arg) || scalar($arg) < 1) {
424         print "destroy requires the object number to destroy.\n";
425         return;
426     }
427
428     print "Destroying object $arg...\n";
429     my $packed = pack("IL", $::client_id, $arg);
430     my $rc = ioctl(DEV_OBD, &OBD_IOC_DESTROY, $packed);
431
432     if (!defined $rc) {
433         print STDERR "ioctl failed: $!\n";
434     } elsif ($rc eq "0 but true") {
435         print "Finished (success)\n";
436     } else {
437         print "ioctl returned error code $rc.\n";
438     }
439 }
440
441 sub Getattr {
442     if (!defined($::client_id)) {
443         print "You must first ``connect''.\n";
444         return;
445     }
446
447     my $inode = shift;
448
449     if (!defined($inode) || scalar($inode) < 1) {
450         print "invalid arguments; type \"help getattr\" for a synopsis\n";
451         return;
452     }
453
454     # see Setattr
455     my $packed = pack("ILsx2lLLLI", $::client_id, $inode, 0, 0, 0, 0, 0, 0, 0,
456                       0);
457     my $rc = ioctl(DEV_OBD, &OBD_IOC_GETATTR, $packed);
458
459     if (!defined $rc) {
460         print STDERR "ioctl failed: $!\n";
461     } elsif ($rc eq "0 but true") {
462         my ($valid, $mode, $uid, $gid, $size, $atime, $mtime, $ctime, $flags);
463         ($valid, $mode, $uid, $gid, $size, $atime, $mtime, $ctime, $flags) =
464           unpack("ISssx2lLLLI", $packed);
465
466         printf("Inode: %d  Mode:  %o\n", $inode, $mode);
467         printf("User: %6d   Group: %6d   Size: %d\n", $uid, $gid, $size);
468         printf("ctime: %08lx -- %s\n", $ctime, scalar(gmtime($ctime)));
469         printf("atime: %08lx -- %s\n", $atime, scalar(gmtime($atime)));
470         printf("mtime: %08lx -- %s\n", $mtime, scalar(gmtime($mtime)));
471         printf("flags: %08x\n", $flags);
472         print "Finished (success)\n";
473     } else {
474         print "ioctl returned error code $rc.\n";
475     }
476 }
477
478 sub Setattr {
479     if (!defined($::client_id)) {
480         print "You must first ``connect''.\n";
481         return;
482     }
483
484     my $inode = shift;
485     my $valid = 0;
486     my $mode = oct(shift);
487     my $uid = shift;
488     my $gid = shift;
489     my $size = shift;
490     my $atime = shift;
491     my $mtime = shift;
492     my $ctime = shift;
493
494     if (defined($uid)) {
495         $valid |= &ATTR_UID;
496     }
497     if (defined($gid)) {
498         $valid |= &ATTR_GID;
499     }
500     if (defined($size)) {
501         $valid |= &ATTR_SIZE;
502     }
503     if (defined($atime)) {
504         $valid |= &ATTR_ATIME;
505     }
506     if (defined($mtime)) {
507         $valid |= &ATTR_MTIME;
508     }
509     if (defined($ctime)) {
510         $valid |= &ATTR_CTIME;
511     }
512     if (defined($mode)) {
513         $valid |= &ATTR_MODE;
514     }
515
516     if (!defined($inode) || scalar($inode) < 1) {
517         print "invalid arguments; type \"help setattr\" for a synopsis\n";
518         return;
519     }
520
521     #struct iattr {
522     #        unsigned int    ia_valid; (32)
523     #        umode_t         ia_mode; (16)
524     #        uid_t           ia_uid; (16)
525     #        gid_t           ia_gid; (16)
526     # -- 16 bit alignment here! --
527     #        off_t           ia_size; (32)
528     #        time_t          ia_atime; (32)
529     #        time_t          ia_mtime; (32)
530     #        time_t          ia_ctime; (32)
531     #        unsigned int    ia_attr_flags; (32)
532     #};
533
534     printf "valid is %x, mode is %o\n", $valid, $mode;
535     my $packed = pack("ILLSssx2ILLLL", $::client_id, $inode, $valid, $mode,
536                       $uid, $gid, $size, $atime, $mtime, $ctime, 0);
537     my $rc = ioctl(DEV_OBD, &OBD_IOC_SETATTR, $packed);
538
539     if (!defined $rc) {
540         print STDERR "ioctl failed: $!\n";
541     } elsif ($rc eq "0 but true") {
542         print "Finished (success)\n";
543     } else {
544         print "ioctl returned error code $rc.\n";
545     }
546 }
547
548 sub Read {
549     if (!defined($::client_id)) {
550         print "You must first ``connect''.\n";
551         return;
552     }
553
554     my $inode = shift;
555     my $count = shift;
556     my $offset = shift;
557   
558     if (!defined($inode) || scalar($inode) < 1 || !defined($count) ||
559         $count < 1 || (defined($offset) && $offset < 0)) {
560         print "invalid arguments; type \"help read\" for a synopsis\n";
561         return;
562     }
563
564     if (!defined($offset)) {
565         $offset = 0;
566     }
567
568     print("Reading $count bytes starting at byte $offset from object " .
569           "$inode...\n");
570
571     # "allocate" a large enough buffer
572     my $buf = sprintf("%${count}s", " ");
573     die "suck" if (length($buf) != $count);
574
575     # the perl we're using doesn't support pack type Q, and offset is 64 bits
576     my $packed = pack("ILpLLL", $::client_id, $inode, $buf, $count, $offset, 0);
577
578     my $rc = ioctl(DEV_OBD, &OBD_IOC_READ, $packed);
579
580     $retval = unpack("l", $packed);
581
582     if (!defined $rc) {
583         print STDERR "ioctl failed: $!\n";
584     } elsif ($rc eq "0 but true") {
585         if ($retval >= 0) {
586                 print substr($buf, 0, $retval);
587                 print "\nRead $retval of an attempted $count bytes.\n";
588                 print "Finished (success)\n";
589         } else {
590                 print "Finished (error $retval)\n";
591         }
592     } else {
593         print "ioctl returned error code $rc.\n";
594     }
595 }
596
597 sub Read2 {
598     if (!defined($::client_id)) {
599         print "You must first ``connect''.\n";
600         return;
601     }
602
603     my $inode = shift;
604     my $count = shift;
605     my $offset = shift;
606   
607     if (!defined($inode) || scalar($inode) < 1 || !defined($count) ||
608         $count < 1 || (defined($offset) && $offset < 0)) {
609         print "invalid arguments; type \"help read\" for a synopsis\n";
610         return;
611     }
612
613     if (!defined($offset)) {
614         $offset = 0;
615     }
616
617     print("Reading $count bytes starting at byte $offset from object " .
618           "$inode...\n");
619
620     # "allocate" a large enough buffer
621     my $buf = sprintf("%${count}s", " ");
622     die "suck" if (length($buf) != $count);
623
624     # the perl we're using doesn't support pack type Q, and offset is 64 bits
625     my $packed = pack("ILpLLL", $::client_id, $inode, $buf, $count, $offset, 0);
626
627     my $rc = ioctl(DEV_OBD, &OBD_IOC_READ2, $packed);
628
629     $retval = unpack("l", $packed);
630
631     if (!defined $rc) {
632         print STDERR "ioctl failed: $!\n";
633     } elsif ($rc eq "0 but true") {
634         if ($retval >= 0) {
635                 print substr($buf, 0, $retval);
636                 print "\nRead $retval of an attempted $count bytes.\n";
637                 print "Finished (success)\n";
638         } else {
639                 print "Finished (error $retval)\n";
640         }
641     } else {
642         print "ioctl returned error code $rc.\n";
643     }
644 }
645
646 sub Write {
647     if (!defined($::client_id)) {
648         print "You must first ``connect''.\n";
649         return;
650     }
651
652     my $inode = shift;
653     my $offset = shift;
654     my $text = join(' ', @_);
655     my $count = length($text);
656
657     if (!defined($inode) || scalar($inode) < 1 || !defined($offset) ||
658         scalar($offset) < 0) {
659         print "invalid arguments; type \"help write\" for a synopsis\n";
660         return;
661     }
662
663     if (!defined($text)) {
664         $text = "";
665         $count = 0;
666     }
667
668     print("Writing $count bytes starting at byte $offset to object " .
669           "$inode...\n");
670
671     # the perl we're using doesn't support pack type Q
672     my $packed = pack("ILpLLL", $::client_id, $inode, $text, $count, $offset, 0);
673     my $rc = ioctl(DEV_OBD, &OBD_IOC_WRITE, $packed);
674
675     $retval = unpack("l", $packed);
676
677     if (!defined $rc) {
678         print STDERR "ioctl failed: $!\n";
679     } elsif ($rc eq "0 but true") {
680         if ($retval >= 0) {
681                 print "\nWrote $retval of an attempted $count bytes.\n";
682                 print "Finished (success)\n";
683         } else {
684                 print "Finished (error $retval)\n";
685         }
686     } else {
687         print "ioctl returned error code $rc.\n";
688     }
689 }
690
691 sub Preallocate {
692     my $arg = shift;
693
694     if (!defined($::client_id)) {
695         print "You must first ``connect''.\n";
696         return;
697     }
698
699     if (!defined($arg) || scalar($arg) < 1 || scalar($arg) > 32) {
700         $arg = 32;
701     }
702
703     print "Preallocating $arg inodes...\n";
704     my $packed = pack("LLx128", $::client_id, $arg);
705     # client id, alloc, inodes[32]
706
707     my $rc = ioctl(DEV_OBD, &OBD_IOC_PREALLOCATE, $packed);
708
709     if (!defined $rc) {
710         print STDERR "ioctl failed: $!\n";
711     } elsif ($rc eq "0 but true") {
712         my $alloc = unpack("x4L", $packed);
713         my @inodes = unpack("x8L32", $packed);
714         my $i;
715
716         print "Got $alloc inodes: ";
717         foreach $i (@inodes) {
718             print $i . " ";
719         }
720         print "\nFinished (success)\n";
721     } else {
722         print "ioctl returned error code $rc.\n";
723     }
724 }
725
726 sub Decusecount {
727     my $rc = ioctl(DEV_OBD, &OBD_IOC_DEC_USE_COUNT, 0);
728
729     if (!defined $rc) {
730         print STDERR "ioctl failed: $!\n";
731     } elsif ($rc eq "0 but true") {
732         print "Finished (success)\n";
733     } else {
734         print "ioctl returned error code $rc.\n";
735     }
736 }
737
738 sub Statfs {
739     if (!defined($::client_id)) {
740         print "You must first ``connect''.\n";
741         return;
742     }
743
744     # struct statfs {
745     #         long f_type;
746     #         long f_bsize;
747     #         long f_blocks;
748     #         long f_bfree;
749     #         long f_bavail;
750     #         long f_files;
751     #         long f_ffree;
752     #         __kernel_fsid_t f_fsid; (64 bits)
753     #         long f_namelen;
754     #         long f_spare[6];
755     # };
756
757     my $packed = pack("LLLLLLLIILL6", $::client_id, 0, 0, 0, 0, 0, 0, 0, 0, 0,
758                       0, 0, 0, 0, 0, 0);
759
760     my $rc = ioctl(DEV_OBD, &OBD_IOC_STATFS, $packed);
761
762     if (!defined $rc) {
763         print STDERR "ioctl failed: $!\n";
764     } elsif ($rc eq "0 but true") {
765         # skip both the conn_id and the fs_type in the buffer
766         my ($bsize, $blocks, $bfree, $bavail, $files, $ffree) =
767             unpack("x4x4LLLLLL", $packed);
768         print("$bsize byte blocks: $blocks, " . ($blocks - $bfree) . " used, " .
769               "$bfree free ($bavail available).\n");
770         print "$files files, " . ($files - $ffree) . " used, $ffree free.\n";
771         print "Finished (success)\n";
772     } else {
773         print "ioctl returned error code $rc.\n";
774     }
775 }
776
777 sub Help {
778     my $arg = shift;
779
780     if ( !$arg || !$commands{$arg} ) {
781         print "Comands: ", join( ' ', @jcm_cmd_list), "\n";
782     } else {
783         print "Usage: " .  $commands{$arg}->{doc} . "\n";
784     }
785 }
786
787 sub Quit {
788     if ($::client_id) {
789         print "Disconnecting active session ($::client_id)...";
790         Disconnect($::client_id);
791     }
792     exit;
793 }