#!/opt/bin/perl -w

use strict;
use warnings;

use Getopt::Long qw( :config no_auto_abbrev bundling );
use Pod::Usage;

use Solaris::DeviceTree;
use Solaris::DeviceTree::OBP qw( :DEFAULT );

use Data::Dumper;

use vars qw( $VERSION );
$VERSION = "0.01";

my $man = 0;
my $help = 0;

my $devtree;
my $bootinfo;
my $aliases;
my $disk;
my $tape;
my $network;
my $businfo;
my $slots;

my %devtreeOptions = (
  attr		=> undef,
  prop		=> undef,
  promprop	=> undef,
  minor		=> undef,
);

my %dpOptions = (
);

GetOptions( 'help|?' => \$help, man => \$man,
  'p|print'	=> \$devtree,
  'w|attr:s'	=> \$devtreeOptions{attr},
  'o|prop:s'	=> \$devtreeOptions{prop},
  'r|promprop:s' => \$devtreeOptions{promprop},
  'm|minor'	=> \$devtreeOptions{minor},
  'v|all'	=> sub { @devtreeOptions{ qw(attr prop promprop minor) } = ('', '', '', '') },

  'a|aliases:s'	=> \$aliases,
  'd|disks'	=> \$disk,
  'n|networks'	=> \$network,
  't|tapes'	=> \$tape,
  'b|bootinfo'	=> \$bootinfo,
  'u|businfo'	=> \$businfo,
  ) or pod2usage( 2 );

pod2usage( 1 ) if( $help );
pod2usage( -exitstatus => 0, -verbose => 2 ) if( $man );

if( $devtree ) {
  print_tree( %devtreeOptions );
} elsif( defined $aliases ) {
  my %options;
  $options{aliases} = $aliases if( $aliases ne '' );
  print_aliases( %options );
} elsif( defined $disk ) {
  print_disk();
} elsif( defined $tape ) {
  print_tape();
} elsif( defined $network ) {
  print_network();
} elsif( defined $bootinfo ) {
  print_bootinfo();
} elsif( defined $businfo ) {
  print_businfo(
    indent => 1,
    node => new Solaris::DeviceTree( use => [ qw( libdevinfo filesystem path_to_inst ) ], ),
  );
} else {
  pod2usage( 1 );
}

# -- Utility functions --

# Returns maximal length of all strings in the array.
sub maxlen {
  return 0 if( @_ == 0 );
  my $max = length shift;
  foreach (@_) {
    $max = length if( $max < length );
  }
  return $max;
}

# -- Print device tree --

# Print the line $line with current width and prepend the first line
# with $prefix1 and all following lines with $prefix2.
# $line must not contain any newlines.
sub printPrefix1 {
  my ($prefix1, $prefix2, $line) = @_;
  my $width = $ENV{COLUMNS} || 80;
  my $maxlen = $width - length( $prefix1 ) - 1;

  if( !defined $line || $line eq '' ) {
    print $prefix1, "\n";
    return;
  }
#my $oldline = $line;
  my $first;
  my $line2;
  ($first, $line2) = ($line =~ /^(.{0,${maxlen}})(?:\s+(.*))?$/);
  if( !defined $first ) {
    ($first, $line) = ($line =~ /^(.{0,${maxlen}})(.*)$/);
  } else {
    $line = $line2;
  }
#print "Line: <$oldline>\n" if( !defined $first );
  print $prefix1, $first, "\n";


  while( $line ) {
    ($first, $line2) = ($line =~ /^(.{0,${maxlen}} )(.*)$/);
    if( !defined $first ) {
      ($first, $line) = ($line =~ /^(.{0,${maxlen}})(.*)$/);
    } else {
      $line = $line2;
    }
    print $prefix2, $first, "\n" if( $first );
  }
}

# Print the line $line with current width and prepend the first line
# with $prefix1 and all following lines with $prefix2.
sub printPrefix {
  my ($prefix1, $prefix2, $line) = @_;
  my @lines = split( /\n/, $line );
  printPrefix1( $prefix1, $prefix2, shift @lines );
  foreach my $line (@lines) {
    printPrefix1( $prefix2, $prefix2, $line );
  }
}

# Print the specified node and recurse further into the tree.
sub printRecursive {
  my ($node, %options) = @_;
  print $node->devfs_path;
  if( $node->can( "sources" ) ) {
    print " (", join( ",", $node->sources ), ")";
  }

  my $ctds = '';
  {
    my $ctrl_node = $node;
    while( defined $ctrl_node && !defined $node->controller ) {
      $ctrl_node = $ctrl_node->parent_node;
    }
    $ctds .= 'c' . $node->controller if( defined $ctrl_node && defined $ctrl_node->controller );
  }
  $ctds .= 't' . $node->target if( defined $node->target );
  $ctds .= 'd' . $node->lun if( defined $node->lun );
  print " [$ctds]" if( $ctds ne '' );
  print "\n";

  if( defined $options{attr} ) {
    print "  Node Name:       ", $node->node_name || '', "\n";
    print "  Binding Name:    ", $node->binding_name || '', "\n";
    print "  Bus Address:     ", (defined $node->bus_addr ? $node->bus_addr : "<unassigned>"), "\n";

    my @cnames = $node->compatible_names;
    if( @cnames && scalar @cnames > 0 ) {
      foreach my $n (@cnames) {
        print "  Compatible Name: $n\n";
      }
    }
    print "  Device ID:       ", $node->devid || '', "\n";
    print "  Driver Name:     ", (defined $node->driver_name ? $node->driver_name : "<unbound>"), "\n";
    my %ops = $node->driver_ops;
    print "  Driver Ops:      ", %ops ? join( " ", keys %ops ) : "<undefined>", "\n";
    print "  Instance:        ", (defined $node->instance ? $node->instance : "<unassigned>"), "\n";
    print "  Node ID:         ", $node->nodeid || '', "\n";
  }

  if( defined $options{prop} ) {
    my $props = $node->props;
    my @listProps = (ref $options{prop} ? @{$options{prop}} : keys %$props);
    # Print only if the node has properties at all *and* some of them should be printed
    print "  Properties:\n" if( keys %$props > 0 && @listProps > 0 );
    my $maxlen = maxlen( @listProps );
    foreach my $propName (sort @listProps) {
      next if( !exists $props->{$propName} );
      my $p = $props->{$propName};
      my ($major, $minor) = $p->devt;
      my $majMinString = (defined $major ? "($major,$minor) " : "" );
      printPrefix( sprintf( "    %-${maxlen}s -> ", $p->name ), " " x ($maxlen + 8),
        $majMinString . join( " ", map { "'" . $_ . "'" } $p->data ) );
    }
  }

  if( defined $options{promprop} ) {
    my $pprops = $node->prom_props;
    my @listProps = (ref $options{promprop} ? @{$options{promprop}} : keys %$pprops);
    my $maxlen = maxlen( @listProps );
    # Print only if the node has properties at all *and* some of them should be printed
    print "  PROM-Properties:\n" if( keys %$pprops > 0 && @listProps > 0 );
    foreach my $ppropname (sort @listProps) {
      next if( !exists $pprops->{$ppropname} );
      my $string = $pprops->{$ppropname}->string;
      printPrefix( sprintf( "    %-${maxlen}s -> ", $ppropname ), " " x ($maxlen + 8), $string );
    }
  }

  if( defined $options{minor} ) {
    my $mn = $node->minor_nodes;
#print Dumper( $mn );
    print "  Minor-Nodes:\n" if( $mn && @$mn > 0 );
    foreach my $m (sort { $a->name cmp $b->name } @$mn) {
      print "  * Name:          ", $m->name || "<undefined>", "\n";
      my ($major, $minor) = $m->devt;
      print "    Devt:          (", defined $major ? $major : "<undefined>", ",", defined $minor ? $minor : "<undefined>", ")\n";
      print "    Nodetype:      ", $m->nodetype || "<undefined>", "\n";
      print "    Spectype:      ", $m->spectype || "<undefined>", "\n";
    }
  }

  foreach my $child (sort { $a->devfs_path cmp $b->devfs_path } $node->child_nodes) {
    printRecursive( $child, %options );
  }
}

# Print the device tree.
sub print_tree {
  my %options = @_;
#  require Solaris::DeviceTree::Libdevinfo;
#  require Solaris::DeviceTree::PathToInst;
#  require Solaris::DeviceTree::Filesystem;
#  my $tree = new Solaris::DeviceTree::Libdevinfo;
#  my $tree = new Solaris::DeviceTree::PathToInst;
#  my $tree = new Solaris::DeviceTree::Filesystem;
  my $tree = new Solaris::DeviceTree( use => [ qw( path_to_inst libdevinfo filesystem ) ] );

  if( defined $options{attr} && $options{attr} ne '' ) {
    $options{prop} = [ split( /,/, $options{attr} ) ];
  }

  if( defined $options{prop} && $options{prop} ne '' ) {
    $options{prop} = [ split( /,/, $options{prop} ) ];
  }

  if( defined $options{promprop} && $options{promprop} ne '' ) {
    $options{promprop} = [ split( /,/, $options{promprop} ) ];
  }

  printRecursive( $tree, %options );

}

# -- Alias --

sub print_aliases {
  my %options = @_;

  require Solaris::DeviceTree::Libdevinfo;
  my $tree = new Solaris::DeviceTree::Libdevinfo;
  my %aliases = %{$tree->aliases};

  if( exists $options{aliases} ) {
    my $name = $options{aliases};
    if( exists $aliases{$name} ) {
      print $aliases{$name}, "\n";
    } else {
      print STDERR "The alias with the name '$name' could not be found.\n";
      exit 1;
    }
  } else {
    my $len = maxlen( keys %aliases );
    foreach my $alias (sort keys %aliases) {
      printf( "%-${len}s -> %s\n", $alias, $aliases{$alias} );
    }
  }
}

# -- Bootinfo --


sub print_bootinfo {
  my %options = @_;

  require Solaris::DeviceTree::Libdevinfo;
  my $tree = new Solaris::DeviceTree::Libdevinfo;
  my $aliases = $tree->aliases;

  print "Bootpath information\n";
  print "--------------------\n\n";

  my $chosen_boot_device = $tree->obp_chosen_boot_device->string;
  print "Last boot device:\n";
  print "   Boot device:  $chosen_boot_device\n";
  my $obpPath = resolve_path( aliases => $aliases, path => $chosen_boot_device );
  print "   OBP path:     ", $obpPath, "\n";
#  my $node = $tree->__solarisPath( $obpPath );
#  print "   Solaris path: ", $node->string, "\n";

  print "\n";

  my $diag_prop = $tree->find_prop( devfs_path => '/options',
                                    prom_prop_name => 'diag-switch?' );
  my $diag_switch = $diag_prop->string;
  print "Diag-Switch: $diag_switch";
  if( $diag_switch eq 'true' ) {
    print " -> Booting from diag-device\n";
  } elsif( $diag_switch eq 'false' ) {
    print " -> Booting from boot-device\n";
  } else {
    print " -> Unknown state\n";
  }

  print "Boot-devices in normal mode:\n";
  foreach my $boot_device ($tree->obp_boot_devices) {
    print "   Boot device:  $boot_device\n";
    my $obpPath = resolve_path( aliases => $aliases, path => $boot_device );
    print "   OBP path:     ", $obpPath, "\n";
#    my $node = $tree->solarisPath( $obpPath );
#    print "   Solaris path: ", $node->string, "\n";
  }
  print "\n";

  print "Boot-devices in diagnostic mode:\n";
  foreach my $diag_device ($tree->obp_diag_devices) {
    print "   Diag device:  $diag_device\n";
    my $obpPath = resolve_path( aliases => $aliases, path => $diag_device );
    print "   OBP path:     ", $obpPath, "\n";
#    my $node = $tree->solarisPath( $obpPath );
#    print "   Solaris path: ", $node->string, "\n";
  }
  print "\n";

}

# -- disk --

sub print_disk {
  require Solaris::DeviceTree;
  my $tree = new Solaris::DeviceTree( use => [ qw( libdevinfo path_to_inst filesystem ) ], );

  # -> TODO: Select wheter all or only accessible disks should be printed
  # Criteria:
  # o has instance in the kernel
  # o has ctds
  foreach my $c (sort { $a->controller <=> $b->controller } $tree->controller_nodes) {
    print "+-";
    print "c", $c->controller if( defined $c->controller );
    print " (", $c->devfs_path, ")\n";
    foreach my $disk (sort { ($a->target || 0)*2 + ($a->lun || 0) <=>
                             ($b->target || 0)*2 + ($b->lun || 0) }
                      $c->block_nodes) {
      next if( !defined $disk->target && !defined $disk->lun );
      print "| +-";
      print "c", $c->controller if( defined $c->controller );
      print "t", $disk->target if( defined $disk->target );
      print "d", $disk->lun if( defined $disk->lun );
      print " (", $disk->devfs_path, ")\n";
    }
  }
}

# -- tape --

sub print_tape {
  require Solaris::DeviceTree;
  my $tree = new Solaris::DeviceTree( use => [ qw( libdevinfo path_to_inst filesystem ) ], );

  # -> TODO: Select wheter all or only accessible tapes should be printed
  # Criteria:
  # o has instance in the kernel
  # o has /dev/rmt
#  foreach my $disk ($tree->block_nodes) {
#    print $disk->devfs_path, "\n";
#  }
}

# -- Network --

sub print_network {
#  require Solaris::DeviceTree;
#  my $tree = new Solaris::DeviceTree( use => [ qw( path_to_inst ) ], );
  require Solaris::DeviceTree::PathToInst;
  my $tree = new Solaris::DeviceTree::PathToInst;

  # -> TODO: Historical network nodes only in /etc/path_to_inst should be honored

  print "The following network devices have been found:\n";
  foreach my $nn (sort { $a->driver_name . $a->instance cmp $b->driver_name . $b->instance }
                  $tree->network_nodes) {
    my $interface = $nn->driver_name . $nn->instance;
    print "+-", $interface, " (", $nn->devfs_path, ")\n";
  }
}

# -- businfo --

sub iaToInt {
  my $result = 0;
  foreach my $i (@_) {
    $result = $result * 256 + $i;
  }
  $result;
}

sub getSpeed {
  my ($props) = @_;

  my $freqstr = undef;
  if( exists $props->{'clock-frequency'} ) {
    my $freq = iaToInt( unpack( "C*", ${$props->{'clock-frequency'}} ) );
    if( $freq < 1000 ) {
      $freqstr = sprintf( "%d Hz", $freq );
    } elsif( $freq >= 1000 && $freq <= 1000000 ) {
      $freqstr = sprintf( "%d KHz", $freq / 1000 );
    } else {
      $freqstr = sprintf( "%d MHz", int( $freq / 1000 ) / 1000 );
    }
  }
  return $freqstr;
}

sub getUPAAddress {
  my $node = shift;
  my $props = $node->prom_props;
  my $portid;

  # From
  #  "source/osnet_volume/usr/src/lib/libprtdiag/common/pdevinfo_sun4u.c" #102 (get_id)
  if( exists $props->{'upa-portid'} ) {
    # Devices on the UPA bus should have a portid
    $portid = iaToInt( unpack( "C*", ${$props->{'upa-portid'}} ) );
  } elsif( exists $props->{'portid'} ) {
    # Devices on the UPA bus should have a portid
    $portid = iaToInt( unpack( "C*", ${$props->{'portid'}} ) );
  } elsif( defined $node->bus_addr ) {
    # If not, use the well known bus adress as a last resort
    # Please note, that the bus address can be undefined (device not on bus)
    # or the empty string (device on the bus but with no specific address).
    $portid = $node->bus_addr;
  } else {
    # Dammit, this device is not on the bus!
    $portid = undef;
  }
  return $portid;
}

# This function formats the output of speed values of the form
#    <number> <unit>
sub formatSpeed {
  my $string = shift;
  return '' if( !defined $string );
  my ($speed, $unit) = ($string =~ /\s*(\d+)\s*(.*)/);
#print "Speed: $speed Unit: $unit\n";
  sprintf( "%3d %3s", $speed, $unit );
}

sub printLine {
  my %args = @_;

#  my $pts = new Solaris::PathToSlot;
#  my %info = (position => '', channel => '', device => '', location => '', $pts->pathToSlot( $args{node}->devfsPath ) );
#  $info{position} .= ': ' if( $info{position} ne '' );
  my $position = '';
#  $position .= $info{position} . $info{device};
#  $position .= " " . $info{channel} if( $info{channel} ne '' );
#  $position .= ": " . $info{location} if( $info{location} ne '' );

  my %state = $args{node}->state;

  my $physicalPath = $args{node}->devfs_path;
  my $port = getUPAAddress( $args{node} );
  # The addition of the node number is needed at least for the CPUs on the E450
  $physicalPath .= '@' . $port if( $physicalPath !~ /@/ && defined $port );
#  my $device = solarisDevice( physicalPath => $physicalPath );
  my $device = '';

  my $props = $args{node}->prom_props;
  my $slot;
  my $reg;
  if( defined $props && exists $props->{'assigned-addresses'} ) {
    $slot = ( iaToInt( unpack( "CCCC", $props->{'assigned-addresses'}) ) >> 11 ) & 0x1f;
  }

  if( defined $props && exists $props->{'reg'} ) {
    $reg = ( iaToInt( unpack( "CCCC", $props->{'reg'}) ) >> 11 ) & 0x1f;
  }

  printf( "%-25s %-21s %10s %-8s %-16s %s",
    $args{path} || '',
#    ($args{driver} || '') . '-' . ($slot || '') . '-' . ($reg || '') . '-' . ($port || ''),
    $args{driver} || '',
    $device || '',
    formatSpeed( $args{speed} ),
    $args{model} || '',
    ($position  || '') . (exists $state{DRIVER_DETACHED} ? ' (detached)' : ''),
  );

  my $node = $args{node};
  my $soldev = $node->solaris_device;
  print " [$soldev]" if( defined $soldev && $soldev ne '' );
  print "\n";
}

sub printUPANode {
  my %args = @_;
  my $node = $args{node};
  die 'node not defined' if( !defined $node );

  my $props = $node->prom_props;
  my $portid = getUPAAddress( $node );

  my $path = "| " x ($args{indent} - 1) . "+-UPA Device";
  $path .= " #" . $portid if( defined $portid );
  my $speed = getSpeed( $props );
  my $driver = $node->node_name;
  if( defined $node->bus_addr ) {
    $driver .= "@" . $node->bus_addr;
  } elsif( defined $portid ) {
    $driver .= "@" . sprintf( "%x", $portid );
  }
  my $model = $props->{'model'}->string if( exists $props->{'model'} );

  printLine( node => $node, path => $path, speed => $speed, driver => $driver, model => $model );
  print_businfo( node => $node, indent => $args{indent} );
}

sub printIDENode {
  my %args = @_;
  my $node = $args{node};
  my $props = $node->prom_props;

  my $nodename = $node->node_name;
  my $busaddress = $node->bus_addr;

  # -> TODO: Use di_state to check if the instance is bound instead of a defined busaddress.
  return if( !defined $busaddress );

  my $path = "| " x ($args{indent} - 1) . "+-IDE Device";
  my $driver = $nodename;
  $driver .= "@" . $busaddress if( defined $busaddress );
  my $speed = getSpeed( $props );

  my $model;	# -> TODO: We should make a vtoc-object here and call vtoc->label

  printLine( node => $node, path => $path, speed => $speed, driver => $driver, model => $model );
}

sub printSCSINode {
  my %args = @_;
  my $node = $args{node};
  my $props = $node->prom_props;

  my $nodename = $node->node_name;
  my $busaddress = $node->bus_addr;

  # -> TODO: This should be done with checking for bound instances through di_state (see source of prtconf)
  return if( !defined $busaddress );

  my $path = "| " x ($args{indent} - 1) . "+-SCSI Device";
  my $driver = $nodename;
  $driver .= "@" . $busaddress if( defined $busaddress );
  my $speed = getSpeed( $props );

  my $model;	# -> TODO: We should make a vtoc-object here and call vtoc->label

  my %state = $node->state;
  # Skip SCSI nodes with detached drivers which have no Solaris name
  my $device = solarisDevice( physicalPath => $node->devfsPath );
#  my $device = '';
  if( !exists $state{DRIVER_DETACHED} || $device ne '' ) {
    printLine( node => $node, path => $path, speed => $speed, driver => $driver, model => $model );
  }
}

sub printPCINode {
  my %args = @_;
  my $node = $args{node};
  my $props = $node->prom_props;
  
#  my $s = "| " x ($args{indent} - 1) . "+-PCI Device";
#  $s .= " " . $node->node_name . "@" . $node->bus_addr;
#  $s .= " " . getSpeed( $props ) if( defined getSpeed( $props ) );

  my $path = "| " x ($args{indent} - 1) . "+-PCI Device";
  my $driver = $node->node_name;
  $driver .= "@" . $node->bus_addr if( defined $node->bus_addr );
  my $speed = getSpeed( $props );

  my $slot;
  my $reg;
  if( exists $props->{'assigned-addresses'} ) {
    $slot = ( iaToInt( unpack( "CCCC", $props->{'assigned-addresses'} ) ) >> 11 ) & 0x1f;
  }

  if( exists $props->{'reg'} ) {
    $reg = ( iaToInt( unpack( "CCCC", $props->{'reg'} ) ) >> 11 ) & 0x1f;
  }

  my $model = '';
  $model = $props->{'model'}->string if( defined $props->{'model'} );

#  $s = "<nodesc>" if( !defined $s );
#  $slot = "<noslot>" if( !defined $slot );
#  $reg = "<noreg>" if( !defined $reg );
#  $model = "<nomodel>" if( !defined $model );

#  my $pts = new Solaris::PathToSlot;
#  my %info = (position => '', channel => '', device => '', $pts->pathToSlot( $node->devfsPath ) );

#  printf( "%-40s %-10s %-10s %-10s %s\n", $s, $slot, $reg, $model,
#    "$info{position} $info{channel} $info{device} " );

  printLine( node => $node, path => $path, speed => $speed, driver => $driver, model => $model );
  print_businfo( node => $node, indent => $args{indent} );
}

sub print_businfo {
  my %args = @_;

  my $node = $args{node};
  my $props = $node->prom_props;
  my $deviceType;
  $deviceType = $props->{'device_type'}->string if( defined $props->{'device_type'} );;
  if( defined $deviceType && $deviceType eq 'upa' ) {
    my $path = "| " x ($args{indent} - 1) . "+-UPA Bus";
    printLine( node => $node, path => $path, speed => getSpeed( $props ), driver => '', model => '' );

    foreach my $n (sort { getUPAAddress( $a ) <=> getUPAAddress( $b ) }
                   grep { defined getUPAAddress( $_ ) && getUPAAddress( $_ ) ne '' }
                   $node->child_nodes) {
      printUPANode( node => $n, indent => $args{indent} + 1 );
    }
  } elsif( defined $deviceType && $deviceType eq 'pci' ) {
    foreach my $n ($node->child_nodes) {
      printPCINode( node => $n, indent => $args{indent} + 1 );
    }
  } elsif( defined $deviceType && $deviceType eq 'ide' ) {
    foreach my $n ($node->child_nodes) {
      printIDENode( node => $n, indent => $args{indent} + 1 );
    }
  } elsif( defined $deviceType && ($deviceType =~ /^(scsi|scsi-2)$/) ) {
    foreach my $n ($node->child_nodes) {
      printSCSINode( node => $n, indent => $args{indent} + 1 );
    }
  } elsif( defined $deviceType ) {
    # This is not a bus. Do peripheral detection instead
    print "| " x $args{indent}, "+-Device: ", $node->driver_name || '', "\n";
  }
}

1;

__END__
=head1 NAME

devtree - Print information about the device tree in Solaris

=head1 SYNOPSIS

 devtree
   -p | --print 
       -v | --all
       -w= [--attr[=attr1,...]]
       -o= [--prop[=prop1,...]]
       -r= [--promprop[=pprop1,...]]
       -m= [--minor]
   -a [<alias>] | --aliases[=<alias>]
   -d | --disks
   -t | --tapes
   -n | --networks
   -b | --bootinfo

=head1 DESCRIPTION


=head1 OPTIONS

=over 4

=item B<-p>, B<--print>

Print the devicetree. Several suboptions are allowed:

=over 4

=item B<-v>, B<--all>

Print all information available for each node in the device tree.
If only specific information is needed you can use the following options:

=item B<-w>, B<--attr[=attr1,...]>

Prints attributes for the device node. If attribute names are specified
only those attributes are printed or all attributes if no attributes
names are specified..

=item B<-o>, B<--prop[=prop1,...]>

Prints properties for the device node. If property names are
specified only those properties are printed or all properties if no
property names are given.

=item B<-r>, B<--promprop[=promprop1,...]>

Prints prom properties for the device node. If property names are
specified only those properties are printed or all properties if no
property names are given.

=item B<-m>, B<--minor>

Prints the minor nodes associated with the device node.

=back

=item B<-a>, B<--aliases[=device]>

Print OpenBoot device aliases. Aliases entered in the nvramrc with
nvramrc?=false are not printed as they are not known to the OBP.
For script usage it is possible to specify the value of a single alias
whose value is output unformatted.

=item B<-b>, B<--bootinfo>

Print information related to booting from the OpenBoot-Prom.
This includes the device last booted from and the boot- and diag-devices.

=item B<-d>, B<--disks>

Prints all disks in the system.

=item B<-n>, B<--network>

Prints all network adapters, regardless if they are plumbed or not.

=back

=head1 EXAMPLES

=over 4

=item B<Print the complete devicetree>

  devtree -pv

=head1 AUTHOR

Dagobert Michelsen, E<lt>dam@baltic-online.deE<gt>


=head1 SEE ALSO

L<Solaris::DeviceTree>

=cut


