4 # This code is issued under the GNU General Public License.
5 # See the file COPYING in this distribution
7 # Copyright (C) 1998, Stelias Computing
9 # Modified for InterMezzo from Gordian's HSM bcache device/jcm module
10 # Copyright (C) 1999, Carnegie Mellon University
12 # Derived from InterMezzo's incontrol, modified for OBD's
13 # Copyright (C) 1999, Stelias Computing
18 BEGIN { require "asm/errno.ph" };
19 BEGIN { require "asm/ioctl.ph" };
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);
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);
77 GetOptions("f!" => \$file, "device=s" => \$::device, "fs=s" => $::filesystem) || die "Getoptions";
80 # get a console for the app
87 ('device' => {func => "Device", doc => "device <dev>: open another OBD device"},
88 'filesystem' => {func => "Filesystem", doc => "filesystem <dev>: partition for direct OBD device"},
89 'create' => {func => "Create", doc => "create: creates a new inode"},
90 'attach' => {func => "Attach", doc => "format type [adapter bus tid lun]"},
91 'format' => {func => "Format", doc => "format type adapter bus tid lun size"},
92 'partition' => {func => "Partition", doc => "partition type adapter bus tid lun partition size"},
93 'setup' => {func => "Setup", doc => "setup: link the ext2 partition (default /dev/loop0) to this obddev"},
94 'connect' => {func => "Connect", doc => "connect: allocates client ID for this session"},
95 'disconnect' => {func => "Disconnect", doc => "disconnect [id]: frees client resources"},
96 'sync' => {func => "Sync", doc => "sync: flushes buffers to disk"},
97 'destroy' => {func => "Destroy", doc => "setup: destroys an inode"},
98 'cleanup' => {func => "Cleanup", doc => "cleanup the minor obd device"},
99 'dec_use_count' => {func => "Decusecount", doc => "decreases the module use count so that the module can be removed following an oops"},
100 'read' => {func => "Read", doc => "read <inode> <count> [offset]"},
101 'fsread' => {func => "Read2", doc => "read <inode> <count> [offset]"},
102 'write' => {func => "Write", doc => "write <inode> <offset> <text>"},
103 'setattr' => {func => "Setattr", doc => "setattr <inode> [mode [uid [gid [size [atime [mtime [ctime]]]]]]]"},
104 'getattr' => {func => "Getattr", doc => "getattr <inode>: displays inode object attributes"},
105 'preallocate' => {func => "Preallocate", doc => "preallocate [num]: requests preallocation of num inodes."},
106 'statfs' => {func => "Statfs", doc => "statfs: filesystem status information"},
107 'help' => {func => \&Help, doc => "help: this message"},
108 'quit' => {func => \&Quit, doc => "see \"exit\""},
109 'exit' => {func => \&Quit, doc => "see \"quit\""}
113 # setup completion function
115 my @jcm_cmd_list = keys %commands;
123 Filesystem($::filesystem);
131 $term = new Term::ReadLine 'obdcontrol ';
132 $attribs = $term->Attribs;
133 $attribs->{attempted_completion_function} = \&completeme;
134 $term->ornaments('md,me,,'); # bold face prompt
136 # make sure stdout is not buffered
137 STDOUT->autoflush(1);
140 # Get on with the show
144 #------------------------------------------------------------------------------
146 my ($text, $line, $start, $end) = @_;
147 if (substr($line, 0, $start) =~ /^\s*$/) {
148 $attribs->{completion_word} = \@jcm_cmd_list;
149 return $term->completion_matches($text,
150 $attribs->{'list_completion_function'});
157 my @completions = completeme($given, $given, 0, length($given));
158 if ($#completions == 0) {
159 $name = shift @completions;
165 # start making requests
168 $line = $term->readline("obdcontrol > ");
176 my @arg = split(' ', $line);
177 my $word = shift @arg;
183 $cmd = find_command($word);
186 printf STDERR "$word: No such command, or not unique.\n";
190 if ($cmd eq "help" || $cmd eq "exit" || $cmd eq "quit") {
191 return (&{$commands{$cmd}->{func}}(@arg));
195 return (&{$commands{$cmd}->{func}}(@arg));
198 # set the object store in the ext2 formatted block device
200 my $filesystem = shift;
201 $filesystem = "/dev/loop0" unless $filesystem;
203 $::filesystem = $filesystem;
204 if (!defined($::st = stat($filesystem))) {
205 die "Unable to stat $filesystem.\n";
209 # select the OBD device we talk to
214 $device = "/dev/obd0";
217 # Open the device, as we need an FD for the ioctl
218 sysopen(DEV_OBD, $device, 0) || die "Cannot open $device";
230 if ($type eq "obdscsi" ) {
235 $data = pack("iiiii", $adapter, $bus, $tid, $lun, $size);
239 my $packed = pack("ipip", length($type), $type, $datalen, $data);
241 my $rc = ioctl(DEV_OBD, &OBD_IOC_ATTACH, $packed);
244 print STDERR "ioctl failed: $!\n";
245 } elsif ($rc eq "0 but true") {
246 print "Finished (success)\n";
248 print "ioctl returned error code $rc.\n";
255 my $data = pack("i", $size);
258 my $packed = pack("ip", $datalen, $data);
259 my $rc = ioctl(DEV_OBD, &OBD_IOC_FORMATOBD, $packed);
262 print STDERR "ioctl failed: $!\n";
263 } elsif ($rc eq "0 but true") {
264 print "Finished (success)\n";
266 print "ioctl returned error code $rc.\n";
274 my $data = pack("ii", $partno, $size);
277 my $packed = pack("ip", $datalen, $data);
278 my $rc = ioctl(DEV_OBD, &OBD_IOC_PARTITION, $packed);
281 print STDERR "ioctl failed: $!\n";
282 } elsif ($rc eq "0 but true") {
283 print "Finished (success)\n";
285 print "ioctl returned error code $rc.\n";
295 $type = "ext2_obd" unless $type;
297 if ( $type eq "ext2_obd" ) {
299 $dev = $::st->rdev() unless $dev;
300 $data = pack("i", $dev);
304 my $packed = pack("ip", $datalen, $data);
305 my $rc = ioctl(DEV_OBD, &OBD_IOC_SETUP_OBDDEV, $packed);
308 print STDERR "ioctl failed: $!\n";
309 } elsif ($rc eq "0 but true") {
310 print "Finished (success)\n";
312 print "ioctl returned error code $rc.\n";
318 my $rc = ioctl(DEV_OBD, &OBD_IOC_CLEANUP, $err);
321 print STDERR "ioctl failed: $!\n";
322 } elsif ($rc eq "0 but true") {
323 print "Finished (success)\n";
326 print "ioctl returned error code $rc.\n";
335 $rc = ioctl(DEV_OBD, &OBD_IOC_CONNECT, $packed);
336 $id = unpack("I", $packed);
339 print STDERR "ioctl failed: $!\n";
340 } elsif ($rc eq "0 but true") {
342 print "Client ID : $id\n";
343 print "Finished (success)\n";
345 print "ioctl returned error code $rc.\n";
357 print "syntax: disconnect [client ID]\n";
358 print "When client ID is not given, the last valid client ID to be returned by a\n";
359 print "connect command this session is used; there is no such ID.\n";
363 my $packed = pack("L", $id);
364 my $rc = ioctl(DEV_OBD, &OBD_IOC_DISCONNECT, $packed);
367 print STDERR "ioctl failed: $!\n";
368 } elsif ($rc eq "0 but true") {
369 $::client_id = undef;
370 print "Finished (success)\n";
372 print "ioctl returned error code $rc.\n";
382 if (defined($quiet) && !($quiet eq "quiet")) {
383 print "syntax: create [number of objects [quiet]]\n";
387 my $packed = pack("IL", $::client_id, $prealloc);
388 if (!defined($arg) || scalar($arg) < 2) {
389 print "Creating 1 object...\n";
390 $rc = ioctl(DEV_OBD, &OBD_IOC_CREATE, $packed);
391 if (!defined($quiet)) {
392 my $ino = unpack("L", $packed);
393 print "Created object #$ino.\n";
398 print "Creating " . scalar($arg) . " objects...\n";
399 for ($i = 0; $i < scalar($arg); $i++) {
400 $rc = ioctl(DEV_OBD, &OBD_IOC_CREATE, $packed);
401 my $ino = unpack("L", $packed);
402 if (!($rc eq "0 but true")) {
404 $packed = pack("IL", $::client_id, $prealloc);
405 } elsif (!defined($quiet)) {
406 $packed = pack("IL", $::client_id, $prealloc);
407 print "Created object #$ino.\n";
413 print STDERR "ioctl failed: $!\n";
414 } elsif ($rc eq "0 but true") {
415 print "Finished (success)\n";
417 print "ioctl returned error code $rc.\n";
423 my $rc = ioctl(DEV_OBD, &OBD_IOC_SYNC, $err);
426 print STDERR "ioctl failed: $!\n";
427 } elsif ($rc eq "0 but true") {
428 print "Finished (success)\n";
430 print "ioctl returned error code $rc.\n";
435 if (!defined($::client_id)) {
436 print "You must first ``connect''.\n";
442 if (!defined($arg) || scalar($arg) < 1) {
443 print "destroy requires the object number to destroy.\n";
447 print "Destroying object $arg...\n";
448 my $packed = pack("IL", $::client_id, $arg);
449 my $rc = ioctl(DEV_OBD, &OBD_IOC_DESTROY, $packed);
452 print STDERR "ioctl failed: $!\n";
453 } elsif ($rc eq "0 but true") {
454 print "Finished (success)\n";
456 print "ioctl returned error code $rc.\n";
461 if (!defined($::client_id)) {
462 print "You must first ``connect''.\n";
468 if (!defined($inode) || scalar($inode) < 1) {
469 print "invalid arguments; type \"help getattr\" for a synopsis\n";
474 my $packed = pack("ILsx2lLLLI", $::client_id, $inode, 0, 0, 0, 0, 0, 0, 0,
476 my $rc = ioctl(DEV_OBD, &OBD_IOC_GETATTR, $packed);
479 print STDERR "ioctl failed: $!\n";
480 } elsif ($rc eq "0 but true") {
481 my ($valid, $mode, $uid, $gid, $size, $atime, $mtime, $ctime, $flags);
482 ($valid, $mode, $uid, $gid, $size, $atime, $mtime, $ctime, $flags) =
483 unpack("ISssx2lLLLI", $packed);
485 printf("Inode: %d Mode: %o\n", $inode, $mode);
486 printf("User: %6d Group: %6d Size: %d\n", $uid, $gid, $size);
487 printf("ctime: %08lx -- %s\n", $ctime, scalar(gmtime($ctime)));
488 printf("atime: %08lx -- %s\n", $atime, scalar(gmtime($atime)));
489 printf("mtime: %08lx -- %s\n", $mtime, scalar(gmtime($mtime)));
490 printf("flags: %08x\n", $flags);
491 print "Finished (success)\n";
493 print "ioctl returned error code $rc.\n";
498 if (!defined($::client_id)) {
499 print "You must first ``connect''.\n";
505 my $mode = oct(shift);
519 if (defined($size)) {
520 $valid |= &ATTR_SIZE;
522 if (defined($atime)) {
523 $valid |= &ATTR_ATIME;
525 if (defined($mtime)) {
526 $valid |= &ATTR_MTIME;
528 if (defined($ctime)) {
529 $valid |= &ATTR_CTIME;
531 if (defined($mode)) {
532 $valid |= &ATTR_MODE;
535 if (!defined($inode) || scalar($inode) < 1) {
536 print "invalid arguments; type \"help setattr\" for a synopsis\n";
541 # unsigned int ia_valid; (32)
542 # umode_t ia_mode; (16)
545 # -- 16 bit alignment here! --
546 # off_t ia_size; (32)
547 # time_t ia_atime; (32)
548 # time_t ia_mtime; (32)
549 # time_t ia_ctime; (32)
550 # unsigned int ia_attr_flags; (32)
553 printf "valid is %x, mode is %o\n", $valid, $mode;
554 my $packed = pack("ILLSssx2ILLLL", $::client_id, $inode, $valid, $mode,
555 $uid, $gid, $size, $atime, $mtime, $ctime, 0);
556 my $rc = ioctl(DEV_OBD, &OBD_IOC_SETATTR, $packed);
559 print STDERR "ioctl failed: $!\n";
560 } elsif ($rc eq "0 but true") {
561 print "Finished (success)\n";
563 print "ioctl returned error code $rc.\n";
568 if (!defined($::client_id)) {
569 print "You must first ``connect''.\n";
577 if (!defined($inode) || scalar($inode) < 1 || !defined($count) ||
578 $count < 1 || (defined($offset) && $offset < 0)) {
579 print "invalid arguments; type \"help read\" for a synopsis\n";
583 if (!defined($offset)) {
587 print("Reading $count bytes starting at byte $offset from object " .
590 # "allocate" a large enough buffer
591 my $buf = sprintf("%${count}s", " ");
592 die "suck" if (length($buf) != $count);
594 # the perl we're using doesn't support pack type Q, and offset is 64 bits
595 my $packed = pack("ILpLLL", $::client_id, $inode, $buf, $count, $offset, 0);
597 my $rc = ioctl(DEV_OBD, &OBD_IOC_READ, $packed);
599 $retval = unpack("l", $packed);
602 print STDERR "ioctl failed: $!\n";
603 } elsif ($rc eq "0 but true") {
605 print substr($buf, 0, $retval);
606 print "\nRead $retval of an attempted $count bytes.\n";
607 print "Finished (success)\n";
609 print "Finished (error $retval)\n";
612 print "ioctl returned error code $rc.\n";
617 if (!defined($::client_id)) {
618 print "You must first ``connect''.\n";
626 if (!defined($inode) || scalar($inode) < 1 || !defined($count) ||
627 $count < 1 || (defined($offset) && $offset < 0)) {
628 print "invalid arguments; type \"help read\" for a synopsis\n";
632 if (!defined($offset)) {
636 print("Reading $count bytes starting at byte $offset from object " .
639 # "allocate" a large enough buffer
640 my $buf = sprintf("%${count}s", " ");
641 die "suck" if (length($buf) != $count);
643 # the perl we're using doesn't support pack type Q, and offset is 64 bits
644 my $packed = pack("ILpLLL", $::client_id, $inode, $buf, $count, $offset, 0);
646 my $rc = ioctl(DEV_OBD, &OBD_IOC_READ2, $packed);
648 $retval = unpack("l", $packed);
651 print STDERR "ioctl failed: $!\n";
652 } elsif ($rc eq "0 but true") {
654 print substr($buf, 0, $retval);
655 print "\nRead $retval of an attempted $count bytes.\n";
656 print "Finished (success)\n";
658 print "Finished (error $retval)\n";
661 print "ioctl returned error code $rc.\n";
666 if (!defined($::client_id)) {
667 print "You must first ``connect''.\n";
673 my $text = join(' ', @_);
674 my $count = length($text);
676 if (!defined($inode) || scalar($inode) < 1 || !defined($offset) ||
677 scalar($offset) < 0) {
678 print "invalid arguments; type \"help write\" for a synopsis\n";
682 if (!defined($text)) {
687 print("Writing $count bytes starting at byte $offset to object " .
690 # the perl we're using doesn't support pack type Q
691 my $packed = pack("ILpLLL", $::client_id, $inode, $text, $count, $offset, 0);
692 my $rc = ioctl(DEV_OBD, &OBD_IOC_WRITE, $packed);
694 $retval = unpack("l", $packed);
697 print STDERR "ioctl failed: $!\n";
698 } elsif ($rc eq "0 but true") {
700 print "\nWrote $retval of an attempted $count bytes.\n";
701 print "Finished (success)\n";
703 print "Finished (error $retval)\n";
706 print "ioctl returned error code $rc.\n";
713 if (!defined($::client_id)) {
714 print "You must first ``connect''.\n";
718 if (!defined($arg) || scalar($arg) < 1 || scalar($arg) > 32) {
722 print "Preallocating $arg inodes...\n";
723 my $packed = pack("LLx128", $::client_id, $arg);
724 # client id, alloc, inodes[32]
726 my $rc = ioctl(DEV_OBD, &OBD_IOC_PREALLOCATE, $packed);
729 print STDERR "ioctl failed: $!\n";
730 } elsif ($rc eq "0 but true") {
731 my $alloc = unpack("x4L", $packed);
732 my @inodes = unpack("x8L32", $packed);
735 print "Got $alloc inodes: ";
736 foreach $i (@inodes) {
739 print "\nFinished (success)\n";
741 print "ioctl returned error code $rc.\n";
746 my $rc = ioctl(DEV_OBD, &OBD_IOC_DEC_USE_COUNT, 0);
749 print STDERR "ioctl failed: $!\n";
750 } elsif ($rc eq "0 but true") {
751 print "Finished (success)\n";
753 print "ioctl returned error code $rc.\n";
758 if (!defined($::client_id)) {
759 print "You must first ``connect''.\n";
771 # __kernel_fsid_t f_fsid; (64 bits)
776 my $packed = pack("LLLLLLLIILL6", $::client_id, 0, 0, 0, 0, 0, 0, 0, 0, 0,
779 my $rc = ioctl(DEV_OBD, &OBD_IOC_STATFS, $packed);
782 print STDERR "ioctl failed: $!\n";
783 } elsif ($rc eq "0 but true") {
784 # skip both the conn_id and the fs_type in the buffer
785 my ($bsize, $blocks, $bfree, $bavail, $files, $ffree) =
786 unpack("x4x4LLLLLL", $packed);
787 print("$bsize byte blocks: $blocks, " . ($blocks - $bfree) . " used, " .
788 "$bfree free ($bavail available).\n");
789 print "$files files, " . ($files - $ffree) . " used, $ffree free.\n";
790 print "Finished (success)\n";
792 print "ioctl returned error code $rc.\n";
799 if ( !$arg || !$commands{$arg} ) {
800 print "Comands: ", join( ' ', @jcm_cmd_list), "\n";
802 print "Usage: " . $commands{$arg}->{doc} . "\n";
808 print "Disconnecting active session ($::client_id)...";
809 Disconnect($::client_id);