Whamcloud - gitweb
New files:
[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_OBDDEV, $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") || $packed == 0) {
384                 last;
385             } elsif (!defined($quiet)) {
386                 print "Created object #$ino.\n";
387             }
388         }
389     }
390
391     if (!defined $rc) {
392         print STDERR "ioctl failed: $!\n";
393     } elsif ($rc eq "0 but true") {
394         print "Finished (success)\n";
395     } else {
396         print "ioctl returned error code $rc.\n";
397     }
398 }
399
400 sub Sync {
401     my $err = "0";
402     my $rc = ioctl(DEV_OBD, &OBD_IOC_SYNC, $err);
403
404     if (!defined $rc) {
405         print STDERR "ioctl failed: $!\n";
406     } elsif ($rc eq "0 but true") {
407         print "Finished (success)\n";
408     } else {
409         print "ioctl returned error code $rc.\n";
410     }
411 }
412
413 sub Destroy {
414     if (!defined($::client_id)) {
415         print "You must first ``connect''.\n";
416         return;
417     }
418
419     my $arg = shift;
420
421     if (!defined($arg) || scalar($arg) < 1) {
422         print "destroy requires the object number to destroy.\n";
423         return;
424     }
425
426     print "Destroying object $arg...\n";
427     my $packed = pack("IL", $::client_id, $arg);
428     my $rc = ioctl(DEV_OBD, &OBD_IOC_DESTROY, $packed);
429
430     if (!defined $rc) {
431         print STDERR "ioctl failed: $!\n";
432     } elsif ($rc eq "0 but true") {
433         print "Finished (success)\n";
434     } else {
435         print "ioctl returned error code $rc.\n";
436     }
437 }
438
439 sub Getattr {
440     if (!defined($::client_id)) {
441         print "You must first ``connect''.\n";
442         return;
443     }
444
445     my $inode = shift;
446
447     if (!defined($inode) || scalar($inode) < 1) {
448         print "invalid arguments; type \"help getattr\" for a synopsis\n";
449         return;
450     }
451
452     # see Setattr
453     my $packed = pack("ILsx2lLLLI", $::client_id, $inode, 0, 0, 0, 0, 0, 0, 0,
454                       0);
455     my $rc = ioctl(DEV_OBD, &OBD_IOC_GETATTR, $packed);
456
457     if (!defined $rc) {
458         print STDERR "ioctl failed: $!\n";
459     } elsif ($rc eq "0 but true") {
460         my ($valid, $mode, $uid, $gid, $size, $atime, $mtime, $ctime, $flags);
461         ($valid, $mode, $uid, $gid, $size, $atime, $mtime, $ctime, $flags) =
462           unpack("ISssx2lLLLI", $packed);
463
464         printf("Inode: %d  Mode:  %o\n", $inode, $mode);
465         printf("User: %6d   Group: %6d   Size: %d\n", $uid, $gid, $size);
466         printf("ctime: %08lx -- %s\n", $ctime, scalar(gmtime($ctime)));
467         printf("atime: %08lx -- %s\n", $atime, scalar(gmtime($atime)));
468         printf("mtime: %08lx -- %s\n", $mtime, scalar(gmtime($mtime)));
469         printf("flags: %08x\n", $flags);
470         print "Finished (success)\n";
471     } else {
472         print "ioctl returned error code $rc.\n";
473     }
474 }
475
476 sub Setattr {
477     if (!defined($::client_id)) {
478         print "You must first ``connect''.\n";
479         return;
480     }
481
482     my $inode = shift;
483     my $valid = 0;
484     my $mode = oct(shift);
485     my $uid = shift;
486     my $gid = shift;
487     my $size = shift;
488     my $atime = shift;
489     my $mtime = shift;
490     my $ctime = shift;
491
492     if (defined($uid)) {
493         $valid |= &ATTR_UID;
494     }
495     if (defined($gid)) {
496         $valid |= &ATTR_GID;
497     }
498     if (defined($size)) {
499         $valid |= &ATTR_SIZE;
500     }
501     if (defined($atime)) {
502         $valid |= &ATTR_ATIME;
503     }
504     if (defined($mtime)) {
505         $valid |= &ATTR_MTIME;
506     }
507     if (defined($ctime)) {
508         $valid |= &ATTR_CTIME;
509     }
510     if (defined($mode)) {
511         $valid |= &ATTR_MODE;
512     }
513
514     if (!defined($inode) || scalar($inode) < 1) {
515         print "invalid arguments; type \"help setattr\" for a synopsis\n";
516         return;
517     }
518
519     #struct iattr {
520     #        unsigned int    ia_valid; (32)
521     #        umode_t         ia_mode; (16)
522     #        uid_t           ia_uid; (16)
523     #        gid_t           ia_gid; (16)
524     # -- 16 bit alignment here! --
525     #        off_t           ia_size; (32)
526     #        time_t          ia_atime; (32)
527     #        time_t          ia_mtime; (32)
528     #        time_t          ia_ctime; (32)
529     #        unsigned int    ia_attr_flags; (32)
530     #};
531
532     printf "valid is %x, mode is %o\n", $valid, $mode;
533     my $packed = pack("ILLSssx2ILLLL", $::client_id, $inode, $valid, $mode,
534                       $uid, $gid, $size, $atime, $mtime, $ctime, 0);
535     my $rc = ioctl(DEV_OBD, &OBD_IOC_SETATTR, $packed);
536
537     if (!defined $rc) {
538         print STDERR "ioctl failed: $!\n";
539     } elsif ($rc eq "0 but true") {
540         print "Finished (success)\n";
541     } else {
542         print "ioctl returned error code $rc.\n";
543     }
544 }
545
546 sub Read {
547     if (!defined($::client_id)) {
548         print "You must first ``connect''.\n";
549         return;
550     }
551
552     my $inode = shift;
553     my $count = shift;
554     my $offset = shift;
555   
556     if (!defined($inode) || scalar($inode) < 1 || !defined($count) ||
557         $count < 1 || (defined($offset) && $offset < 0)) {
558         print "invalid arguments; type \"help read\" for a synopsis\n";
559         return;
560     }
561
562     if (!defined($offset)) {
563         $offset = 0;
564     }
565
566     print("Reading $count bytes starting at byte $offset from object " .
567           "$inode...\n");
568
569     # "allocate" a large enough buffer
570     my $buf = sprintf("%${count}s", " ");
571     die "suck" if (length($buf) != $count);
572
573     # the perl we're using doesn't support pack type Q, and offset is 64 bits
574     my $packed = pack("ILpLLL", $::client_id, $inode, $buf, $count, $offset, 0);
575
576     my $rc = ioctl(DEV_OBD, &OBD_IOC_READ, $packed);
577
578     $retval = unpack("l", $packed);
579
580     if (!defined $rc) {
581         print STDERR "ioctl failed: $!\n";
582     } elsif ($rc eq "0 but true") {
583         if ($retval >= 0) {
584                 print substr($buf, 0, $retval);
585                 print "\nRead $retval of an attempted $count bytes.\n";
586                 print "Finished (success)\n";
587         } else {
588                 print "Finished (error $retval)\n";
589         }
590     } else {
591         print "ioctl returned error code $rc.\n";
592     }
593 }
594
595 sub Read2 {
596     if (!defined($::client_id)) {
597         print "You must first ``connect''.\n";
598         return;
599     }
600
601     my $inode = shift;
602     my $count = shift;
603     my $offset = shift;
604   
605     if (!defined($inode) || scalar($inode) < 1 || !defined($count) ||
606         $count < 1 || (defined($offset) && $offset < 0)) {
607         print "invalid arguments; type \"help read\" for a synopsis\n";
608         return;
609     }
610
611     if (!defined($offset)) {
612         $offset = 0;
613     }
614
615     print("Reading $count bytes starting at byte $offset from object " .
616           "$inode...\n");
617
618     # "allocate" a large enough buffer
619     my $buf = sprintf("%${count}s", " ");
620     die "suck" if (length($buf) != $count);
621
622     # the perl we're using doesn't support pack type Q, and offset is 64 bits
623     my $packed = pack("ILpLLL", $::client_id, $inode, $buf, $count, $offset, 0);
624
625     my $rc = ioctl(DEV_OBD, &OBD_IOC_READ2, $packed);
626
627     $retval = unpack("l", $packed);
628
629     if (!defined $rc) {
630         print STDERR "ioctl failed: $!\n";
631     } elsif ($rc eq "0 but true") {
632         if ($retval >= 0) {
633                 print substr($buf, 0, $retval);
634                 print "\nRead $retval of an attempted $count bytes.\n";
635                 print "Finished (success)\n";
636         } else {
637                 print "Finished (error $retval)\n";
638         }
639     } else {
640         print "ioctl returned error code $rc.\n";
641     }
642 }
643
644 sub Write {
645     if (!defined($::client_id)) {
646         print "You must first ``connect''.\n";
647         return;
648     }
649
650     my $inode = shift;
651     my $offset = shift;
652     my $text = join(' ', @_);
653     my $count = length($text);
654
655     if (!defined($inode) || scalar($inode) < 1 || !defined($offset) ||
656         scalar($offset) < 0) {
657         print "invalid arguments; type \"help write\" for a synopsis\n";
658         return;
659     }
660
661     if (!defined($text)) {
662         $text = "";
663         $count = 0;
664     }
665
666     print("Writing $count bytes starting at byte $offset to object " .
667           "$inode...\n");
668
669     # the perl we're using doesn't support pack type Q
670     my $packed = pack("ILpLLL", $::client_id, $inode, $text, $count, $offset, 0);
671     my $rc = ioctl(DEV_OBD, &OBD_IOC_WRITE, $packed);
672
673     $retval = unpack("l", $packed);
674
675     if (!defined $rc) {
676         print STDERR "ioctl failed: $!\n";
677     } elsif ($rc eq "0 but true") {
678         if ($retval >= 0) {
679                 print "\nWrote $retval of an attempted $count bytes.\n";
680                 print "Finished (success)\n";
681         } else {
682                 print "Finished (error $retval)\n";
683         }
684     } else {
685         print "ioctl returned error code $rc.\n";
686     }
687 }
688
689 sub Preallocate {
690     my $arg = shift;
691
692     if (!defined($::client_id)) {
693         print "You must first ``connect''.\n";
694         return;
695     }
696
697     if (!defined($arg) || scalar($arg) < 1 || scalar($arg) > 32) {
698         $arg = 32;
699     }
700
701     print "Preallocating $arg inodes...\n";
702     my $packed = pack("LLx128", $::client_id, $arg);
703     # client id, alloc, inodes[32]
704
705     my $rc = ioctl(DEV_OBD, &OBD_IOC_PREALLOCATE, $packed);
706
707     if (!defined $rc) {
708         print STDERR "ioctl failed: $!\n";
709     } elsif ($rc eq "0 but true") {
710         my $alloc = unpack("x4L", $packed);
711         my @inodes = unpack("x8L32", $packed);
712         my $i;
713
714         print "Got $alloc inodes: ";
715         foreach $i (@inodes) {
716             print $i . " ";
717         }
718         print "\nFinished (success)\n";
719     } else {
720         print "ioctl returned error code $rc.\n";
721     }
722 }
723
724 sub Decusecount {
725     my $rc = ioctl(DEV_OBD, &OBD_IOC_DEC_USE_COUNT, 0);
726
727     if (!defined $rc) {
728         print STDERR "ioctl failed: $!\n";
729     } elsif ($rc eq "0 but true") {
730         print "Finished (success)\n";
731     } else {
732         print "ioctl returned error code $rc.\n";
733     }
734 }
735
736 sub Statfs {
737     if (!defined($::client_id)) {
738         print "You must first ``connect''.\n";
739         return;
740     }
741
742     # struct statfs {
743     #         long f_type;
744     #         long f_bsize;
745     #         long f_blocks;
746     #         long f_bfree;
747     #         long f_bavail;
748     #         long f_files;
749     #         long f_ffree;
750     #         __kernel_fsid_t f_fsid; (64 bits)
751     #         long f_namelen;
752     #         long f_spare[6];
753     # };
754
755     my $packed = pack("LLLLLLLIILL6", $::client_id, 0, 0, 0, 0, 0, 0, 0, 0, 0,
756                       0, 0, 0, 0, 0, 0);
757
758     my $rc = ioctl(DEV_OBD, &OBD_IOC_STATFS, $packed);
759
760     if (!defined $rc) {
761         print STDERR "ioctl failed: $!\n";
762     } elsif ($rc eq "0 but true") {
763         # skip both the conn_id and the fs_type in the buffer
764         my ($bsize, $blocks, $bfree, $bavail, $files, $ffree) =
765             unpack("x4x4LLLLLL", $packed);
766         print("$bsize byte blocks: $blocks, " . ($blocks - $bfree) . " used, " .
767               "$bfree free ($bavail available).\n");
768         print "$files files, " . ($files - $ffree) . " used, $ffree free.\n";
769         print "Finished (success)\n";
770     } else {
771         print "ioctl returned error code $rc.\n";
772     }
773 }
774
775 sub Help {
776     my $arg = shift;
777
778     if ( !$arg || !$commands{$arg} ) {
779         print "Comands: ", join( ' ', @jcm_cmd_list), "\n";
780     } else {
781         print "Usage: " .  $commands{$arg}->{doc} . "\n";
782     }
783 }
784
785 sub Quit {
786     if ($::client_id) {
787         print "Disconnecting active session ($::client_id)...";
788         Disconnect($::client_id);
789     }
790     exit;
791 }