| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | #***************************************************************************** | 
| 2 |  |  |  |  |  |  | #*                                                                           * | 
| 3 |  |  |  |  |  |  | #*                          Gellyfish Software                               * | 
| 4 |  |  |  |  |  |  | #*                                                                           * | 
| 5 |  |  |  |  |  |  | #*                                                                           * | 
| 6 |  |  |  |  |  |  | #***************************************************************************** | 
| 7 |  |  |  |  |  |  | #*                                                                           * | 
| 8 |  |  |  |  |  |  | #*      PROGRAM     :  Net::SNMP::Interfaces.                                * | 
| 9 |  |  |  |  |  |  | #*                                                                           * | 
| 10 |  |  |  |  |  |  | #*      AUTHOR      :  JNS                                                   * | 
| 11 |  |  |  |  |  |  | #*                                                                           * | 
| 12 |  |  |  |  |  |  | #*      DESCRIPTION :  Simple SNMP stuff for Interfaces.                     * | 
| 13 |  |  |  |  |  |  | #*                                                                           * | 
| 14 |  |  |  |  |  |  | #*                                                                           * | 
| 15 |  |  |  |  |  |  | #***************************************************************************** | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | package Net::SNMP::Interfaces; | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | =head1 NAME | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | Net::SNMP::Interfaces - provide simple methods to gain interface data via | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | SNMP | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | use Net::SNMP::Interfaces; | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  | my $interfaces = Net::SNMP::Interfaces->new(Hostname => 'localhost', | 
| 30 |  |  |  |  |  |  | Community => 'public' ); | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | my @ifnames = $interfaces->all_interfaces(); | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | Net::SNMP::Interfaces aims to provide simple object methods to obtain | 
| 37 |  |  |  |  |  |  | information about a host's network interfaces ( be it a server a router | 
| 38 |  |  |  |  |  |  | or whatever ).  The motivation was largely to allow a programmer to use | 
| 39 |  |  |  |  |  |  | SNMP to obtain this information without needing to know a great deal | 
| 40 |  |  |  |  |  |  | about the gory details. | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | The module uses Net::SNMP under the hood to do the dirty work although | 
| 43 |  |  |  |  |  |  | the user shouldn't have to worry about that ( the Net::SNMP object is | 
| 44 |  |  |  |  |  |  | available though for those who might feel the need ). | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | The actual details for a particular interface are obtained from the methods | 
| 47 |  |  |  |  |  |  | of Net::SNMP::Interfaces::Details - objects of which type can be obtained | 
| 48 |  |  |  |  |  |  | for the methods all_interfaces() and interface(). | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | Of course the simpler interface has its limitations and there may well | 
| 51 |  |  |  |  |  |  | be things that you would like to do which you cant do with this module - | 
| 52 |  |  |  |  |  |  | in which case I would recommend that you get a good book on SNMP and | 
| 53 |  |  |  |  |  |  | use Net::SNMP :) | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | The module uses blocking SNMP requests at the current time so if some | 
| 56 |  |  |  |  |  |  | of the methods are taking too long you may want to time them out | 
| 57 |  |  |  |  |  |  | yourself using alarm(). | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | =cut | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  |  | 
| 63 | 1 |  |  | 1 |  | 895 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 39 |  | 
| 64 | 1 |  |  | 1 |  | 1348 | use Net::SNMP; | 
|  | 1 |  |  |  |  | 465034 |  | 
|  | 1 |  |  |  |  | 114 |  | 
| 65 | 1 |  |  | 1 |  | 19 | use Carp; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 58 |  | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  |  | 
| 68 | 1 |  |  | 1 |  | 724 | use Net::SNMP::Interfaces::Details; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 35 |  | 
| 69 |  |  |  |  |  |  |  | 
| 70 | 1 |  |  |  |  | 786 | use vars qw( | 
| 71 |  |  |  |  |  |  | @ISA | 
| 72 |  |  |  |  |  |  | $VERSION | 
| 73 |  |  |  |  |  |  | $AUTOLOAD | 
| 74 | 1 |  |  | 1 |  | 5 | ); | 
|  | 1 |  |  |  |  | 1 |  | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  |  | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | ($VERSION) = q$Revision: 1.4 $ =~ /([\d.]+)/; | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | =head2 METHODS | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | =over | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | =item new(  HASH %args ) | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | The constructor of the class. It takes several arguments that are passed | 
| 87 |  |  |  |  |  |  | to Net::SNMP : | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | =over | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | =item Hostname | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  | The name of the host which you want to connect to. Defaults to 'localhost'. | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | =item Community | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | The SNMP community string which you want to use for this session.  The default | 
| 98 |  |  |  |  |  |  | is 'public'. | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | =item Port | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | The UDP port that the SNMP service is listening on.  The default is 161. | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | =item Version | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | The SNMP version (as described in the L documentation) to be | 
| 107 |  |  |  |  |  |  | used.  The default is 'snmpv1'.  Support for SNMPv3 is currently somehwat | 
| 108 |  |  |  |  |  |  | limited. | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | =back | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | There is a also an optional argument 'RaiseError' which determines | 
| 113 |  |  |  |  |  |  | the behaviour of the module in the event there is an error while creating | 
| 114 |  |  |  |  |  |  | the SNMP.  Normally new() will return undef if there was an error but if | 
| 115 |  |  |  |  |  |  | RaiseError is set to a true value it will die() printing the error string | 
| 116 |  |  |  |  |  |  | to STDERR.  If this is not set and an error occurs undef will be return | 
| 117 |  |  |  |  |  |  | and the variable $Net::SNMP::Interfaces::error will contain the test of | 
| 118 |  |  |  |  |  |  | the error. | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | Because the interfaces are discovered in the constructor, if the module | 
| 121 |  |  |  |  |  |  | is to be used in a long running program to monitor a host where | 
| 122 |  |  |  |  |  |  | interfaces might be added or removed it is recommended that the object | 
| 123 |  |  |  |  |  |  | returned by new() is periodically destroyed and a new one constructed. | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | =cut | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | sub new | 
| 129 |  |  |  |  |  |  | { | 
| 130 | 0 |  |  | 0 | 1 |  | my ( $proto , %args ) = @_; | 
| 131 |  |  |  |  |  |  |  | 
| 132 | 0 |  |  |  |  |  | my $self = {}; | 
| 133 |  |  |  |  |  |  |  | 
| 134 | 0 |  | 0 |  |  |  | $self->{_hostname}  = $args{Hostname}  || 'localhost'; | 
| 135 | 0 |  | 0 |  |  |  | $self->{_community} = $args{Community} || 'public'; | 
| 136 | 0 |  | 0 |  |  |  | $self->{_port}      = $args{Port}      || 161; | 
| 137 | 0 |  | 0 |  |  |  | $self->{_version}   = $args{Version}   || 'snmpv1', | 
|  |  |  | 0 |  |  |  |  | 
| 138 |  |  |  |  |  |  | $self->{_raise}     = $args{RaiseError} || 0; | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  |  | 
| 141 | 0 |  |  |  |  |  | my ($session, $error) = Net::SNMP->session( | 
| 142 |  |  |  |  |  |  | -hostname  => $self->{_hostname}, | 
| 143 |  |  |  |  |  |  | -community => $self->{_community}, | 
| 144 |  |  |  |  |  |  | -port      => $self->{_port}, | 
| 145 |  |  |  |  |  |  | -version   => $self->{_version}, | 
| 146 |  |  |  |  |  |  | ); | 
| 147 |  |  |  |  |  |  |  | 
| 148 | 0 | 0 |  |  |  |  | if (!defined($session)) | 
| 149 |  |  |  |  |  |  | { | 
| 150 | 0 | 0 |  |  |  |  | if ( $self->{_raise} ) | 
| 151 |  |  |  |  |  |  | { | 
| 152 | 0 |  |  |  |  |  | croak sprintf("%s: %s", __PACKAGE__, $error); | 
| 153 |  |  |  |  |  |  | } | 
| 154 |  |  |  |  |  |  | else | 
| 155 |  |  |  |  |  |  | { | 
| 156 | 0 |  |  |  |  |  | $Net::SNMP::Interfaces::error = $error; | 
| 157 | 0 |  |  |  |  |  | return undef; | 
| 158 |  |  |  |  |  |  | } | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  |  | 
| 161 | 0 |  |  |  |  |  | $self->{_snmp_session} = $session; | 
| 162 |  |  |  |  |  |  |  | 
| 163 | 0 |  |  |  |  |  | my $ifIndex = '1.3.6.1.2.1.2.2.1.1'; | 
| 164 | 0 |  |  |  |  |  | my $ifDescr = '1.3.6.1.2.1.2.2.1.2'; | 
| 165 |  |  |  |  |  |  |  | 
| 166 | 0 |  |  |  |  |  | my $response; | 
| 167 |  |  |  |  |  |  |  | 
| 168 | 0 | 0 |  |  |  |  | if (!defined($response = $session->get_table($ifIndex))) | 
| 169 |  |  |  |  |  |  | { | 
| 170 | 0 | 0 |  |  |  |  | if ( $self->{_raise} ) | 
| 171 |  |  |  |  |  |  | { | 
| 172 | 0 |  |  |  |  |  | $session->close; | 
| 173 | 0 |  |  |  |  |  | croak sprintf("%s: %s",__PACKAGE__, $session->error); | 
| 174 |  |  |  |  |  |  | } | 
| 175 |  |  |  |  |  |  | else | 
| 176 |  |  |  |  |  |  | { | 
| 177 | 0 |  |  |  |  |  | $Net::SNMP::Interfaces::error = $session->error(); | 
| 178 | 0 |  |  |  |  |  | return undef; | 
| 179 |  |  |  |  |  |  | } | 
| 180 |  |  |  |  |  |  | } | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  |  | 
| 183 | 0 |  |  |  |  |  | foreach my $index ( values %{$response} ) | 
|  | 0 |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | { | 
| 185 | 0 |  |  |  |  |  | my $this_desc = "$ifDescr.$index"; | 
| 186 |  |  |  |  |  |  |  | 
| 187 | 0 |  |  |  |  |  | my $description; | 
| 188 |  |  |  |  |  |  |  | 
| 189 | 0 | 0 |  |  |  |  | if ( defined( $description = $session->get_request($this_desc)) ) | 
| 190 |  |  |  |  |  |  | { | 
| 191 | 0 |  |  |  |  |  | $self->{_desc2index}->{$description->{$this_desc}} = $index; | 
| 192 | 0 |  |  |  |  |  | $self->{_index2desc}->{$index} = $description->{$this_desc}; | 
| 193 |  |  |  |  |  |  | } | 
| 194 |  |  |  |  |  |  | else | 
| 195 |  |  |  |  |  |  | { | 
| 196 | 0 |  |  |  |  |  | $self->{_lasterror} = $session->error(); | 
| 197 |  |  |  |  |  |  | } | 
| 198 |  |  |  |  |  |  | } | 
| 199 |  |  |  |  |  |  |  | 
| 200 | 0 |  |  |  |  |  | return bless $self, $proto; | 
| 201 |  |  |  |  |  |  | } | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  |  |  |  | =item if_names() | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | Returns a list of the interface names. | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | =cut | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | sub if_names | 
| 210 |  |  |  |  |  |  | { | 
| 211 | 0 |  |  | 0 | 1 |  | my ( $self ) = @_; | 
| 212 |  |  |  |  |  |  |  | 
| 213 | 0 |  |  |  |  |  | return keys %{$self->{_desc2index}}; | 
|  | 0 |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | } | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | =item if_indices() | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | Returns a list of the indices of the interfaces - this probably shouldn't | 
| 220 |  |  |  |  |  |  | be necessary but is here for completeness anyway.  If you dont know what | 
| 221 |  |  |  |  |  |  | the index is for you are safe to ignore this. | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | =cut | 
| 224 |  |  |  |  |  |  |  | 
| 225 |  |  |  |  |  |  | sub if_indices | 
| 226 |  |  |  |  |  |  | { | 
| 227 | 0 |  |  | 0 | 1 |  | my ( $self ) = @_; | 
| 228 |  |  |  |  |  |  |  | 
| 229 | 0 |  |  |  |  |  | return keys %{$self->{_index2desc}}; | 
|  | 0 |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | } | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | =item  error() | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | Returns the text of the last Net::SNMP error.  This method only makes sense | 
| 235 |  |  |  |  |  |  | if the previous method call indicated an error by a false return. | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | =cut | 
| 238 |  |  |  |  |  |  |  | 
| 239 |  |  |  |  |  |  | sub error | 
| 240 |  |  |  |  |  |  | { | 
| 241 | 0 |  |  | 0 | 1 |  | my ($self ) = @_; | 
| 242 |  |  |  |  |  |  |  | 
| 243 | 0 |  | 0 |  |  |  | return $self->{_lasterror} || $self->session()->error(); | 
| 244 |  |  |  |  |  |  | } | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | =item session() | 
| 247 |  |  |  |  |  |  |  | 
| 248 |  |  |  |  |  |  | Returns the Net::SNMP session object for this instance. Or a false value | 
| 249 |  |  |  |  |  |  | if there is no open session.  This might be used to call methods on the | 
| 250 |  |  |  |  |  |  | Net::SNMP object if some facility is needed that isnt supplied by this | 
| 251 |  |  |  |  |  |  | module. | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | =cut | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | sub session | 
| 256 |  |  |  |  |  |  | { | 
| 257 | 0 |  |  | 0 | 1 |  | my ( $self ) = @_; | 
| 258 |  |  |  |  |  |  |  | 
| 259 | 0 | 0 |  |  |  |  | return exists $self->{_snmp_session} ? $self->{_snmp_session} : undef; | 
| 260 |  |  |  |  |  |  | } | 
| 261 |  |  |  |  |  |  |  | 
| 262 |  |  |  |  |  |  | =item all_interfaces() | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | Returns a list of Net::SNMP::Interface::Details objects corresponding to | 
| 265 |  |  |  |  |  |  | the interfaces discovered on this host.  In scalar context it will return | 
| 266 |  |  |  |  |  |  | a reference to an array. | 
| 267 |  |  |  |  |  |  |  | 
| 268 |  |  |  |  |  |  | =cut | 
| 269 |  |  |  |  |  |  |  | 
| 270 |  |  |  |  |  |  | sub all_interfaces | 
| 271 |  |  |  |  |  |  | { | 
| 272 | 0 |  |  | 0 | 1 |  | my ( $self ) = @_; | 
| 273 |  |  |  |  |  |  |  | 
| 274 | 0 |  |  |  |  |  | my @interfaces; | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  |  | 
| 277 | 0 |  |  |  |  |  | for my $index ( sort $self->if_indices() ) | 
| 278 |  |  |  |  |  |  | { | 
| 279 | 0 |  |  |  |  |  | my %args = ( | 
| 280 |  |  |  |  |  |  | Index   => $index, | 
| 281 |  |  |  |  |  |  | Name    => $self->{_index2desc}->{$index}, | 
| 282 |  |  |  |  |  |  | Session => $self->session() | 
| 283 |  |  |  |  |  |  | ); | 
| 284 |  |  |  |  |  |  |  | 
| 285 | 0 |  |  |  |  |  | push @interfaces, Net::SNMP::Interfaces::Details->new(%args); | 
| 286 |  |  |  |  |  |  | } | 
| 287 |  |  |  |  |  |  |  | 
| 288 | 0 | 0 |  |  |  |  | return wantarray ? @interfaces : \@interfaces; | 
| 289 |  |  |  |  |  |  | } | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | =item interface( SCALAR $name ) | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | Returns a Net::SNMP::Interfaces::Details object for the named interface. | 
| 294 |  |  |  |  |  |  | Returns undef if the supplied name is not a known interface. | 
| 295 |  |  |  |  |  |  |  | 
| 296 |  |  |  |  |  |  | =cut | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | sub interface | 
| 299 |  |  |  |  |  |  | { | 
| 300 | 0 |  |  | 0 | 1 |  | my ( $self, $name ) = @_; | 
| 301 |  |  |  |  |  |  |  | 
| 302 | 0 |  |  |  |  |  | my $index = $self->{_desc2index}->{$name}; | 
| 303 |  |  |  |  |  |  |  | 
| 304 | 0 | 0 |  |  |  |  | if ( defined $index ) | 
| 305 |  |  |  |  |  |  | { | 
| 306 | 0 |  |  |  |  |  | return Net::SNMP::Interfaces::Details->new( | 
| 307 |  |  |  |  |  |  | Name    => $name, | 
| 308 |  |  |  |  |  |  | Index   => $index, | 
| 309 |  |  |  |  |  |  | Session => $self->session() | 
| 310 |  |  |  |  |  |  | ); | 
| 311 |  |  |  |  |  |  | } | 
| 312 |  |  |  |  |  |  | else | 
| 313 |  |  |  |  |  |  | { | 
| 314 | 0 |  |  |  |  |  | return undef; | 
| 315 |  |  |  |  |  |  | } | 
| 316 |  |  |  |  |  |  | } | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  | =for pod | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | In addition to the methods above, you can also use the methods from | 
| 321 |  |  |  |  |  |  | Net::SNMP::Interfaces::Details but with the addition of the interface | 
| 322 |  |  |  |  |  |  | name as an argument. e.g: | 
| 323 |  |  |  |  |  |  |  | 
| 324 |  |  |  |  |  |  | $in_octs = $self->ifInOctets('eth0'); | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | Please see the documentation for Net::SNMP::Interfaces::Details for more | 
| 327 |  |  |  |  |  |  | on these methods. | 
| 328 |  |  |  |  |  |  |  | 
| 329 |  |  |  |  |  |  | =cut | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | sub AUTOLOAD | 
| 332 |  |  |  |  |  |  | { | 
| 333 | 0 |  |  | 0 |  |  | my ( $self, $name ) = @_; | 
| 334 |  |  |  |  |  |  |  | 
| 335 | 0 | 0 |  |  |  |  | return if $AUTOLOAD =~ /DESTROY$/; | 
| 336 |  |  |  |  |  |  |  | 
| 337 | 0 | 0 |  |  |  |  | croak "No name" unless $name; | 
| 338 | 0 | 0 |  |  |  |  | return undef unless exists $self->{_desc2index}->{$name}; | 
| 339 |  |  |  |  |  |  |  | 
| 340 | 0 |  |  |  |  |  | my ($meth)  = $AUTOLOAD =~ /::([^:]+)$/; | 
| 341 |  |  |  |  |  |  |  | 
| 342 | 1 |  |  | 1 |  | 5 | no strict 'refs'; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 106 |  | 
| 343 |  |  |  |  |  |  |  | 
| 344 | 0 |  |  |  |  |  | *{$AUTOLOAD} = sub { | 
| 345 | 0 |  |  | 0 |  |  | my ( $self, $name ) = @_; | 
| 346 | 0 |  |  |  |  |  | return $self->interface($name)->$meth() ; | 
| 347 | 0 |  |  |  |  |  | }; | 
| 348 |  |  |  |  |  |  |  | 
| 349 | 0 |  |  |  |  |  | goto &{$AUTOLOAD}; | 
|  | 0 |  |  |  |  |  |  | 
| 350 |  |  |  |  |  |  |  | 
| 351 |  |  |  |  |  |  | } | 
| 352 |  |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  |  | 
| 354 |  |  |  |  |  |  | sub DESTROY | 
| 355 |  |  |  |  |  |  | { | 
| 356 | 0 |  |  | 0 |  |  | my ( $self ) = @_; | 
| 357 |  |  |  |  |  |  |  | 
| 358 | 0 |  |  |  |  |  | $self->session()->close(); | 
| 359 |  |  |  |  |  |  | } | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | 1; | 
| 362 |  |  |  |  |  |  | __END__ |