Solaris::DeviceTree::Filesystem - Perl interface to C</dev> and C</devices>


Solaris-DeviceTree documentation Contained in the Solaris-DeviceTree distribution.

Index


Code Index:

NAME

Top

Solaris::DeviceTree::Filesystem - Perl interface to /dev and /devices

SYNOPSIS

Top

  use Solaris::DeviceTree::Filesystem;
  $tree = Solaris::DeviceTree::Filesystem->new;
  @children = $node->child_nodes;
  $devfs_path = $node->devfs_path;
  $node_name = $node->node_name;
  $bus_addr = $node->bus_addr;
  @minor_nodes = @{$node->minor_nodes}
  $instance = $node->instance;




DESCRIPTION

Top

The Solaris::DeviceTree::Filesystem module implements access to the Solaris device configuration files below /devices via a hierarchical tree structure. The API of this class contains all methods from Solaris::DeviceTree::Node applicable to this context.

Additionally, the information from /dev/cfg, /dev/dsk, /dev/rdsk and /dev/rmt is used to identify controller numbers and recognize disk- and tape-devices for instance calculation.

Each directory represents a node in the devicetree, each block or character special file an associated minor node. Other types of files are not allowed below /devices. A name of a special file in the devicetree always has the form

  <node_name>@<bus_addr>:<device_arguments>

The bus address and the device arguments are optional.

METHODS

Top

For a detailed description of the available methods and their meaning see the documentation of the base class Solaris::DeviceTree::Node.

The following methods returns values other than the defaults from the base class:

new

This method contructs a new filesystem tree.

child_nodes

This methods returns the child nodes below this node.

devfs_path

This method returns the physical path for this node.

node_name

This method returns the name of the node. It is undefined for the root node and guaranteed to be defined for all other nodes.

bus_addr

This method return the bus address of the node. The bus address may be undefined.

minor_nodes

This method returns a reference to a list of the minor nodes associated with this node. For a detailed description of the methods available to access the returned minor nodes see Solaris::DeviceTree::Filesystem::MinorNode.

instance

This method returns the instance number of the driver for this node. The instance number is calculated from the minor numbers of the minor nodes for the used driver. The type of the calculation depends on the implementation of the driver. If the mapping of minor numbers to instances is not known in this method undef is returned. Currently undef is returned for all minor numbers for all drivers.

AUTHOR

Top

Copyright 1999-2003 Dagobert Michelsen.

SEE ALSO

Top

  L<Solaris::DeviceTree>, L<Solaris::DeviceTree::Filesystem::MinorNode>


Solaris-DeviceTree documentation Contained in the Solaris-DeviceTree distribution.
#
# $Header: /cvsroot/devicetool/Solaris-DeviceTree/lib/Solaris/DeviceTree/Filesystem.pm,v 1.10 2003/12/12 11:11:55 honkbude Exp $
#

package Solaris::DeviceTree::Filesystem;

use 5.006;
use strict;
use warnings;
use Carp;

our %EXPORT_TAGS = ( 'all' => [ qw() ], );
our @EXPORT = ( @{ $EXPORT_TAGS{'all'} } );

use base qw( Exporter );
our $VERSION = do { my @r = (q$Revision: 1.10 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; # must be all one line, for MakeMaker

our @ISA = qw( Solaris::DeviceTree::Node );
our $_ROOT_NODE;

use Solaris::DeviceTree::Node;
use Solaris::DeviceTree::Filesystem::MinorNode;

# -> TODO: It would be nice to allow other filesystem roots
#          (e. g. /a) for remote mounted filesystems

sub new {
  my ($pkg, %options) = @_;

  if( !defined $_ROOT_NODE ) {

    $_ROOT_NODE = $pkg->_new_node;
    $_ROOT_NODE->{_dir} = "/devices";
    $_ROOT_NODE->{_physical_name} = '';
    $_ROOT_NODE->{_minor_nodes} = [];
  }

  my %disks;

  {
    local *DIR;
    opendir DIR, '/dev/dsk';
    my @dsk = grep !/^\.\.?$/, readdir( DIR );
    @disks{ map { '/dev/dsk/' . $_ } @dsk } = @dsk;
    closedir DIR;

    opendir DIR, '/dev/rdsk';
    my @rdsk = grep !/^\.\.?$/, readdir( DIR );
    @disks{ map { '/dev/rdsk/' . $_ } @rdsk } = @rdsk;
    closedir DIR;
  }

  foreach my $disk (keys %disks) {
    my $devfs_path = readlink $disk;
    my ($c, $t, $d, $s) = ($disks{$disk} =~ /c(\d+)t(\d+)d(\d+)s(\d+)/);
    $devfs_path =~ s!^\.\./\.\./devices!!;
    my ($path, $minor) = ($devfs_path =~ /^([^:]+):(.*)$/);
    my $node = $_ROOT_NODE->find_nodes( devfs_path => $path );
    if( defined $node ) {
      my $parent = $node->parent_node;
      if( defined $parent ) {
        $parent->controller( _controller => $c );
      } else {
        warn "Parent node for the device path '${path}' for the disk " . 
          "'${disk}' could not be found.";
      }

      $node->target( _target => $t );
      $node->lun( _lun => $d );

      my $minor_node = $node->find_minor_node( name => $minor );
      if( defined $minor_node ) {
        $minor_node->slice( _slice => $s );
      } else {
        warn "The minor node '${minor}' for the device path " .
          "'${path}' for the disk " . 
          "'${disk}' could not be found.";
      }
    } else {
      warn "The device path '${path}' for the disk " . 
        "'${disk}' could not be found.";
    }
  }

  my @cfg;
  {
    # -> TODO: A real filehandle would be nicer
    local *DIR;
    opendir DIR, "/dev/cfg";
    @cfg = grep !/^\.\.?$/, readdir( DIR );
    closedir DIR;
  }

  foreach my $cfg (@cfg) {
    next if( $cfg =~ /^usb\d+$/ );	# USB busses are not handled right now -> TODO
    my $devfs_path = readlink "/dev/cfg/" . $cfg;
    if( !defined $devfs_path ) {
      warn "The file '/dev/cfg/${cfg}' is not a link. Skipping it.";
      next;
    }
    my ($c) = ($cfg =~ /c(\d+)/);
    if( !defined $c ) {
      warn "File with peculiar name '${cfg}' found below /dev/cfg.\n" .
        "The names should begin with 'c' and be followed by a number." .
        "Skipping it.";
      next;
    }

    my $ctrl_found = 0;

    $devfs_path =~ s!^\.\./\.\./devices!!;
    if( defined $devfs_path ) {
      my ($path, $minor) = ($devfs_path =~ /^([^:]+):(.*)$/);
      if( defined $path && defined $minor ) {
        my $node = $_ROOT_NODE->find_nodes( devfs_path => $path );
        if( defined $node ) {
          $node->controller( _controller => $c );
          $ctrl_found = 1;
        }
      }
    }
    if( !$ctrl_found ) {
      warn "The node for the device path '${devfs_path}' for the controller " . 
        "'c${c}' could not be found";
    }
  }

  return $_ROOT_NODE;
}

sub child_nodes {
  my ($this, %options) = @_;

  if( !$this->{_child_initialized} ) {
    # Child nodes are created on demand for each node
    if( exists $this->{_dir} ) {
      my %child_nodes;
      local *DIR;
      opendir DIR, $this->{_dir};
      while( defined( my $file = readdir DIR ) ) {
        next if( $file =~ /^\.\.?$/ );
    
        my $filepath = $this->{_dir} . '/' . $file;
        if( !-d $filepath && !-b _ && !-c _ ) {
          warn "File $filepath is neiter a directory nor a block- or\n" .
            "character device and should not belong here!\n";
          next;
        }
  
        # We have now a valid node
        my ($physical_name, $node_name, $bus_addr, $device_arguments) =
          ($file =~ /^
                        (						# physical name
                            ([^\@:]*)					# node name
                            (?:\@([^:]*))?				# bus addr
                        )
                        (?::(.*))?					# device arguments
                        $/x);
  
        my $nodeid = $physical_name;
        if( !exists $child_nodes{$nodeid} ) {
          my $child = $this->_new_node( parent => $this );
          $child->{_physical_name} = $this->{_physical_name} . '/' . $physical_name;
          $child->{_node_name} = $node_name;
          $child->{_bus_addr} = $bus_addr;
          $child->{_minor_nodes} = [];
  
          $child_nodes{$nodeid} = $child;
        }
  
        my $child = $child_nodes{$nodeid};
        if( -d _ ) {
          $child->{_dir} = $filepath;
        }
  
        if( -b _ || -c _ ) {
          # Minor nodes
          my $minor_node = Solaris::DeviceTree::Filesystem::MinorNode->new(
            $filepath, $child );
          push @{$child->{_minor_nodes}}, $minor_node;
        }
      }
      closedir DIR;
    }
  
    $this->{_child_initialized} = 1;
  }

  return $this->SUPER::child_nodes( %options );
}

sub devfs_path {
  my ($this, %options) = @_;

  # Handle special case for root node name
  return !defined $this->parent_node ? '/' : $this->{_physical_name};
}

sub node_name {
  my ($this, %options) = @_;

  return $this->{_node_name};
}

sub bus_addr {
  my ($this, %options) = @_;

  return $this->{_bus_addr};
}

sub minor_nodes {
  my ($this, %options) = @_;

  return $this->{_minor_nodes};
}

# - a disk is a device located below /dev/dsk, /dev/rdsk or one of following:
#     sd ssd
sub is_block_device {
  return 0;
}
# -> TODO

# - a tape is a device located below /dev/rmt or one of the following:
#     st
sub is_byte_device {
  return 0;
}

# Device types taken from 1275.pdf p. 26
#   display
#   block	hard disk	bootable
#   byte	tape		bootable
#   network	ethernet	bootable
#   serial

# Instances can be guessed from the minor nodes. Lookup through
# /etc/name_to_major and hardwiring the driver usage seems
# necessary.

# -> TODO: Should is_disk_device also be implemented in this class?

sub instance {
  my ($this, %options) = @_;

  if( !exists $this->{_instance} ) {
    my $instance = undef;
    if( $this->is_block_device ) {
      foreach my $minor_node ($this->minor_nodes) {
        my ($major, $minor) = $minor_node->devt;
        $instance = int $minor / 8 if( !defined $instance );
      }
    } elsif( $this->is_byte_device ) {
      my $minor = 0;
      # From mtio.7i:
      #     15      7      6          5          4         3          2       1      0
      #     __________________________________________________________________________
      #      Unit #       BSD      Reserved   Density   Density   No rewind    Unit #
      #     Bits 7-15   behavior              Select    Select    on Close    Bits 0-1
      #
      #     /*
      #      * Layout of minor device byte:
      #      */
      #     #define MTUNIT(dev)     (((minor(dev) & 0xff80) >> 5) +(minor(dev) & 0x3))
      #     #define MT_NOREWIND     (1 <<2)
      #     #define MT_DENSITY_MASK (3 <<3)
      #     #define MT_DENSITY1     (0 <<3)         /* Lowest density/format */
      #     #define MT_DENSITY2     (1 <<3)
      #     #define MT_DENSITY3     (2 <<3)
      #     #define MT_DENSITY4     (3 <<3)         /* Highest density/format */
      #     #define MTMINOR(unit)   (((unit & 0x7fc) << 5) + (unit & 0x3))
      #     #define MT_BSD          (1 <<6)         /* BSD behavior on close */

      $instance = ($minor & 0xff80) >> 5 + ($minor & 3);
    }

    $this->{_instance} = $instance;
  }

  return $this->{_instance};
}


1;