[BACK]Return to Trango.pm CVS log [TXT][DIR] Up to [local] / trango / Net-Telnet-Trango / lib / Net / Telnet

Annotation of trango/Net-Telnet-Trango/lib/Net/Telnet/Trango.pm, Revision 1.23

1.14      andrew      1: package Net::Telnet::Trango;
1.22      andrew      2: # $RedRiver: Trango.pm,v 1.21 2006/10/05 17:10:39 andrew Exp $
1.14      andrew      3: use strict;
                      4: use warnings;
                      5: use base 'Net::Telnet';
                      6:
                      7: =pod
                      8:
                      9: =head1 NAME
                     10:
                     11: Net::Telnet::Trango - Perl extension for accessing the Trango telnet interface
                     12:
                     13: =head1 SYNOPSIS
                     14:
                     15:   use Net::Telnet::Trango;
                     16:   my $t = new Net::Telnet::Trango ( Timeout => 5 );
                     17:
                     18:   $t->open( Host => $fox ) or die "Error connecting: $!";
                     19:
                     20:   $t->login('password') or die "Couldn't log in: $!";
                     21:
                     22:   # Do whatever
                     23:
                     24:   $t->exit;
                     25:   $t->close;
                     26:
                     27: =head1 DESCRIPTION
                     28:
                     29: Perl access to the telnet interface on Trango Foxes, SUs and APs.
                     30:
1.20      andrew     31: Another handy feature is that it will parse the output from certain
                     32: commands that is in the format "[key1] value1 [key2] value2" and put
                     33: those in a hashref that is returned.  This makes using the output from
                     34: things like sysinfo very easy to do.
1.14      andrew     35:
                     36: =head2 EXPORT
                     37:
                     38: None
                     39:
                     40: =cut
                     41:
                     42: our $VERSION = '0.01';
                     43:
                     44: my %PRIVATE = (
                     45:   is_connected => 0,
                     46:   logged_in => 0,
                     47: );
                     48:
                     49: =pod
                     50:
                     51: =item new
                     52:
                     53: Same as new from L<Net::Telnet> but has defaults for the trango 'Prompt'
                     54:
                     55: It also takes an optional parameter 'Decode'.  If not defined it
                     56: defaults to 1, if it is set to 0, it will not decode the output and
                     57: instead return an array of the lines that were returned from the
                     58: command.
                     59:
                     60: =cut
                     61:
                     62: sub new
                     63: {
                     64:   my $class = shift;
                     65:
                     66:   my %args;
                     67:   if (@_ == 1) {
                     68:     $args{'Host'} = shift;
                     69:   } else {
                     70:     %args = @_;
                     71:   }
                     72:
                     73:   $args{'Prompt'}  ||= '/#> *$/';
                     74:
                     75:   foreach my $key (keys %args) {
                     76:     $PRIVATE{$key} = $args{$key};
                     77:   }
                     78:   $PRIVATE{'Decode'} = 1 unless defined $PRIVATE{'Decode'};
                     79:   delete $args{'Decode'};
                     80:
                     81:   my $self = $class->SUPER::new(%args);
                     82:   bless $self if ref $self;
                     83:
                     84:   return $self;
                     85: }
                     86:
                     87: #  _password <new password> <new password>
                     88: #  ? [command]
                     89: #  apsearch <secs> <ch#> <h|v> [<ch#> <h|v>]...
                     90: #  arp -bcast <on|off>
                     91: #  bcastscant <all|suid> <ch#> <h|v> [<ch#> <h|v> ...
                     92: #  bye
                     93: #  cf2cf ap [default|<size>]
                     94: #  date
                     95: #  date <month> <day> <year>
                     96: #  freq scantable
                     97: #  freq channeltable
                     98: #  freq writescan [<ch#> <h|v>]
                     99: #  freq writechannel [<ch#> <freq>] ...
                    100: #  freq <ch #> <h|v>
                    101: #  help [command]
                    102: #  heater [<on temp> <off temp>]
                    103: #  ipconfig [<new ip> <new subnet mask> <new gateway>]
                    104: #  log [<# of entries, 1..179>]
                    105: #  log <sum> <# of entries, 1..179>
                    106: #  logout
                    107: #  opmode [ap [y]]
                    108: #  password
                    109: #  ping <ip addr>
                    110: #  polar <h|v>
                    111: #  power <setism|setunii> <max|min|<dBm>>
                    112: #  reboot
                    113: #  restart
                    114: #  remarks [<str>]
                    115: #  rfrxthreshold [<ism|unii> <-90|-85|-80|-75|-70|-65>]
                    116: #  rfrxth [<ism|unii> <-90|-85|-80|-75|-70|-65>]
                    117: #  sysinfo
                    118: #  set suid <id>
                    119: #  set apid <id>
                    120: #  set baseid <id>
                    121: #  set defaultopmode [<ap|su> <min,0..10>]
                    122: #  set defaultopmode off
                    123: #  set snmpcomm [<read | write | trap (id or setall)> <str>]
                    124: #  set mir [on|off]
                    125: #  set mir threshold <kbps>
                    126: #  set rssitarget [<ism|unii> <dBm>]
                    127: #  set serviceradius [<ism | unii> <miles>]
                    128: #  ssrssi <ch #> <h|v>
                    129: #  su [<suid>|all]
                    130: #  su changechannel <all|suid> <ch#> <h|v>
                    131: #  su ipconfig <suid> <new ip> <new subnet> <new gateway>
                    132: #  su [live|poweroff|priority]
                    133: #  su <ping|info|status> <suid>
                    134: #  su powerleveling <all|suid>
                    135: #  su reboot <all|suid>
                    136: #  su restart <all|suid>
                    137: #  su testrflink <all|suid> [r]
                    138: #  su testrflink <setlen> [64..1600]
                    139: #  su testrflink <aptx> [20..100]
                    140: #  su sw <suid|all> <sw #> <on|off>
                    141: #  sudb [dload | view]
                    142: #  sudb add <suid> pr <cir,kbps> <mir,kbps> <device id,hex>
                    143: #  sudb add <suid> reg <cir,kbps> <mir,kbps> <device id,hex>
                    144: #  sudb delete <all|<suid>>
                    145: #  sudb modify <suid> <cir|mir> <kbps>
                    146: #  sudb modify <suid> <su2su> <group id,hex>
                    147: #  sudb view
                    148: #  sulog [lastmins | sampleperiod <1..60>]
                    149: #  sulog [<# of entry,1..18>]
                    150: #  survey <ism|unii> <time, sec> <h|v>
                    151: #  sw [<sw #> <on|off>]
                    152: #  temp
                    153: #  tftpd [on|off]
                    154: #  time
                    155: #  time <hour> <min> <sec>
                    156: #  save <mainimage|fpgaimage> <current chksum> <new chksum>
                    157: #  save <systemsetting|sudb>
                    158: #  updateflash <mainimage|fpgaimage> <current chksum> <new chksum>
                    159: #  updateflash <systemsetting|sudb>
                    160:
                    161: =pod
                    162:
                    163: =head1 METHODS
                    164:
                    165: =head2 ACCESSORS
                    166:
                    167: =over
                    168:
                    169: =item Host
                    170:
                    171: returns the name of the host that you are accessing
                    172:
                    173: =item firmware_version
                    174:
                    175: returns the firmware version on the trango if available otherwise undef.
                    176: Available after a successful open()
                    177: This is usually only set internally
                    178:
                    179: =item host_type
                    180:
                    181: returns the type of host from the login banner for example M5830S or M5300S.
                    182: Available after a successful open()
                    183: This is usually only set internally
                    184:
                    185: =item is_connected
                    186:
                    187: returns 1 after a successful open() otherwise undef
                    188: This is usually only set internally
                    189:
                    190: =item logged_in
                    191:
                    192: returns 1 after a successful login() 0 if it failed and undef if
                    193: login() was never called
                    194: This is usually only set internally
                    195:
                    196: =item login_banner
                    197:
1.20      andrew    198: returns the banner that is displayed when first connected at login.
                    199: Only set after a successful open()
1.14      andrew    200:
                    201: This is usually only set internally
                    202:
                    203: =item last_lines
                    204:
                    205: returns the output from the last cmd() that was run as an array ref
                    206: This is usually only set internally
                    207:
                    208: =back
                    209:
                    210: =head2 ALIASES
                    211:
                    212: =over
                    213:
                    214: =item bye
                    215:
                    216: alias of exit()
                    217:
                    218: =item restart
                    219:
                    220: alias of reboot()
                    221:
                    222: =back
                    223:
                    224: =head2 COMMANDS
                    225:
1.19      andrew    226: Most of these are just shortcuts to C<cmd(String =E<gt> METHOD)>,
                    227: as such they accept the same options as C<cmd()>.
                    228: Specifically they take a named paramater "args", for example:
1.14      andrew    229: C<tftpd(args =E<gt> 'on')> would enable tftpd
                    230:
                    231: =over
                    232:
                    233: =item tftpd
                    234:
                    235: Returns a hash ref of the decoded output from the command.
                    236:
                    237: Also see enable_tftpd() and disable_tftpd() as those check for correct output
                    238:
                    239: =item ver
                    240:
                    241: Returns a hash ref of the decoded output from the command.
                    242:
                    243: =item sysinfo
                    244:
                    245: Returns a hash ref of the decoded output from the command.
                    246:
                    247: =item exit
                    248:
                    249: exits the command session with the trango and closes the connection
                    250:
                    251: =item reboot
                    252:
                    253: reboots the trango and closes the connection
                    254:
1.17      andrew    255: =item remarks
                    256:
1.18      andrew    257: Takes an optional argument, which sets the remarks.
                    258: If there is no argument, returns the current remarks.
1.17      andrew    259:
1.14      andrew    260: =item sulog
                    261:
                    262: returns an array ref of hashes containing each log line.
                    263:
                    264: =item save_sudb
                    265:
                    266: returns true on success, undef on failure
                    267:
                    268: =item syslog
                    269:
                    270: returns the output from the syslog command
                    271:
                    272: =item pipe
                    273:
                    274: returns the output from the pipe command
                    275:
                    276: =item maclist
                    277:
                    278: returns the output from the maclist command
                    279:
                    280: =item maclist_reset
                    281:
                    282: resets the maclist.  No useful output.
                    283:
                    284: =item eth_list
                    285:
                    286: returns the output from the eth list command
                    287:
1.17      andrew    288: =item su_info
                    289:
                    290: You need to pass in args => <suid> and it will return the info for that suid.
                    291:
                    292: =item save_ss
                    293:
                    294: saves the config.  Returns 1 on success, undef on failure.
                    295:
1.14      andrew    296: =cut
                    297:
                    298:
                    299: my $success = 'Success.';
                    300: my %COMMANDS = (
                    301:   tftpd       => { decode => 'all',   expect => $success },
                    302:   ver         => { decode => 'all' },
                    303:   sysinfo     => { decode => 'all',   expect => $success },
                    304:   updateflash => { decode => 'all',   expect => $success },
                    305:   sulog       => { decode => 'sulog', expect => $success },
                    306:   'exit'      => { no_prompt => 1, cmd_disconnects => 1 },
                    307:   reboot      => { no_prompt => 1, cmd_disconnects => 1 },
1.17      andrew    308:   remarks     => { decode => 'all', expect => $success },
1.14      andrew    309:   save_sudb   => { String => 'save sudb', expect => $success },
                    310:   syslog      => { expect => $success },
                    311:   'pipe'      => { }, # XXX needs a special decode
                    312:   maclist     => { decode => 'maclist' },
                    313:   maclist_reset => { String => 'maclist reset', expect => 'done' },
                    314:   eth_link    => { String => 'eth link', expect => $success },
1.17      andrew    315:   su_info     => { String => 'su info', decode => 'all', expect => $success },
                    316:   save_ss     => { String => 'save ss', expect => $success },
1.21      andrew    317:   opmode      => { decode => 'all',   expect => $success },
1.14      andrew    318:   # eth r, w and reset???
                    319:   #su password???
                    320:   #_bootloader
                    321:   #temp
                    322:   #heater
                    323: );
                    324:
                    325: my %ALIASES = (
                    326:   bye     => 'exit',
                    327:   restart => 'reboot',
                    328: );
                    329:
                    330: my %ACCESS = map { $_ => 1 } qw(
                    331:   firmware_version
                    332:   host_type
                    333:   Host
                    334:   is_connected
                    335:   logged_in
                    336:   login_banner
                    337:   Timeout
                    338:   last_lines
                    339:   last_vals
                    340: );
                    341:
                    342: sub AUTOLOAD
                    343: {
                    344:   my $self = shift;
                    345:
                    346:   my ($method) = (our $AUTOLOAD) =~ /^.*::(\w+)$/
                    347:     or die "Weird: $AUTOLOAD";
                    348:
                    349:   if (exists $ALIASES{$method}) {
                    350:     $method = $ALIASES{$method};
                    351:     return $self->$method(@_);
                    352:   }
                    353:
                    354:   if (exists $COMMANDS{$method}) {
                    355:     $COMMANDS{$method}{'String'} ||= $method;
1.17      andrew    356:     $COMMANDS{$method}{'args'} .= ' ' . shift if (@_ == 1);
1.14      andrew    357:     return $self->cmd(%{ $COMMANDS{$method} }, @_);
                    358:   }
                    359:
                    360:   if (exists $ACCESS{$method}) {
                    361:     my $prev = $PRIVATE{$method};
                    362:     ($PRIVATE{$method}) = @_ if @_;
                    363:     return $prev;
                    364:   }
                    365:
                    366:   $method = "SUPER::$method";
                    367:   return $self->$method(@_);
                    368: }
                    369:
                    370: =pod
                    371:
                    372: =item open
                    373:
1.20      andrew    374: Calls Net::Telnet::open() then makes sure you get a password prompt so
                    375: you are ready to login() and parses the login banner so you can get
                    376: host_type() and firmware_version()
1.14      andrew    377:
                    378: =cut
                    379:
                    380: sub open
                    381: {
                    382:   my $self = shift;
                    383:
                    384:   unless ( $self->SUPER::open(@_) ) {
                    385:     #$! = "Couldn't connect to " . $self->Host . ":  $!";
                    386:     return undef;
                    387:   }
                    388:
                    389:   ## Get to login prompt
                    390:   unless ($self->waitfor(
                    391:       -match => '/password: ?$/i',
                    392:       -errmode => "return",
                    393:     ) ) {
                    394:     #$! = "problem connecting to host (" . $self->Host . "): " .
                    395:     #    $self->lastline;
                    396:     return undef;
                    397:   }
                    398:
                    399:   $self->parse_login_banner($self->lastline);
                    400:
                    401:   $self->is_connected(1);
                    402:
                    403:   return $self->is_connected;
                    404: }
                    405:
                    406: =pod
                    407:
                    408: =item login
                    409:
1.20      andrew    410: Calls open() if not already connected, then sends the password and sets
                    411: logged_in() if successful
1.14      andrew    412:
                    413: =cut
                    414:
                    415: sub login
                    416: {
                    417:   my $self = shift;
                    418:
                    419:   unless ($self->is_connected) {
                    420:     $self->open or return undef;
                    421:   }
                    422:
                    423:   my $password = shift;
                    424:
                    425:   $self->print($password);
                    426:   unless ($self->waitfor(
                    427:     -match => $self->prompt,
                    428:     -errmode => "return",
                    429:   ) ) {
                    430:     #$! = "login ($self->Host) failed: " . $self->lastline;
                    431:     return undef;
                    432:   }
                    433:
                    434:   $self->logged_in(1);
                    435:
                    436:   return $self->logged_in;
                    437: }
                    438:
                    439: =pod
                    440:
                    441: =item parse_login_banner
                    442:
1.20      andrew    443: Takes a login banner (what you get when you first connect to the Trango)
                    444: or reads what is already in login_banner() then parses it and sets
                    445: host_type() and firmware_version() as well as login_banner()
1.14      andrew    446:
                    447: =cut
                    448:
                    449: sub parse_login_banner
                    450: {
                    451:   my $self = shift;
                    452:
                    453:   if (@_) {
                    454:     $self->login_banner(@_);
                    455:   }
                    456:
                    457:   my $banner = $self->login_banner;
                    458:
                    459:   my ($type, $ver) = $banner =~
                    460:     /Welcome to Trango Broadband Wireless (\S+)[\s-]+(.+)$/i;
                    461:
                    462:   $self->login_banner($banner);
                    463:   $self->host_type($type);
                    464:   $self->firmware_version($ver);
                    465:
                    466:   return 1;
                    467: }
                    468:
                    469: =pod
                    470:
                    471: =item su_password
                    472:
1.23    ! mike      473: C<su_password('new_password'[, 'suid'])> If no suid is specified,
        !           474: the default is "all".
1.14      andrew    475:
                    476: =cut
                    477:
                    478: sub su_password
                    479: {
                    480:   my $self     = shift;
                    481:   my $new_pass = shift || '';
1.23    ! mike      482:   my $su       = shift || 'all';
1.14      andrew    483:
                    484:   unless (defined $new_pass) {
                    485:     warn "No new password!"
                    486:     #return undef;
                    487:   }
                    488:
                    489:   return $self->cmd(String => 'su password ' .
                    490:                      $su . ' ' .
                    491:                      $new_pass . ' ' .
                    492:                      $new_pass,
                    493:                      expect => $success,
                    494:                     );
                    495: }
                    496:
                    497: =pod
                    498:
                    499: =item su_ipconfig
                    500:
                    501: C<su_ipconfig( 'suid', 'new_ip', 'new_subnet', 'new_gateway' )>
                    502:
                    503: =cut
                    504:
                    505: sub su_ipconfig
                    506: {
                    507:        my $self        = shift;
                    508:
                    509:        my $suid        = shift;
                    510:        my $new_ip      = shift;
                    511:        my $new_subnet  = shift;
                    512:        my $new_gateway = shift;
                    513:
                    514:        return undef unless $suid =~ /^\d+$/;
                    515:        return undef unless $new_ip;
                    516:        return undef unless $new_subnet;
                    517:        return undef unless $new_gateway;
                    518:
                    519:        # su ipconfig <suid> <new ip> <new subnet> <new gateway>
                    520:        return $self->cmd(String => 'su ipconfig ' .
                    521:                      $suid       . ' ' .
                    522:                      $new_ip     . ' ' .
                    523:                      $new_subnet . ' ' .
                    524:                      $new_gateway,
                    525:                      expect => $success,
                    526:                     );
                    527: }
                    528:
                    529: =pod
                    530:
                    531: =item sudb_view
                    532:
                    533: returns a reference to an array of hashes each containing:
                    534:
                    535:   suid
                    536:   type
                    537:   cir
                    538:   mir
                    539:   mac
                    540:
                    541: =cut
                    542:
                    543: sub sudb_view
                    544: {
                    545:   my $self = shift;
                    546:
                    547:   my @lines = $self->cmd( String => 'sudb view', expect => $success );
                    548:
                    549:   return undef unless @lines;
                    550:
                    551:   unless ($PRIVATE{'Decode'}) {
                    552:     return @lines;
                    553:   }
                    554:
                    555:   my @sus;
                    556:   foreach (@lines) {
                    557:     next unless $_;
                    558:     if (/^\[(\d+)\]\s+(\d+)\s+(\d+)\s+(\d+)\s+([0-9A-Fa-f\s]+)$/) {
                    559:       my %s = (
                    560:         suid => $1,
                    561:         type => $2,
                    562:         cir  => $3,
                    563:         mir  => $4,
                    564:         mac  => $5,
                    565:       );
                    566:
                    567:          $s{'mac'} =~ s/\s//g;
                    568:          $s{'mac'} = uc($s{'mac'});
                    569:
                    570:       push @sus, \%s;
                    571:     }
                    572:   }
                    573:
                    574:   return \@sus;
                    575: }
                    576:
                    577: =pod
                    578:
                    579: =item sudb_add
                    580:
                    581: Takes the following paramaters
                    582:
                    583:        suid : numeric,
                    584:        type : (reg|pr)
                    585:        cir  : numeric,
                    586:        mir  : numeric,
                    587:        mac  : Almost any format, it will be reformatted,
                    588:
                    589: and returns true on success or undef otherwise.
                    590:
                    591: You should save_sudb() after calling this, or your changes  will be lost
                    592: when the AP is rebooted.
                    593:
                    594: =cut
                    595:
                    596: sub sudb_add
                    597: {
                    598:        my $self = shift;
                    599:        my $suid = shift;
                    600:        my $type = shift;
                    601:        my $cir  = shift;
                    602:        my $mir  = shift;
                    603:        my $mac  = shift;
                    604:
                    605:        if ($suid =~ /\D/) {
                    606:                return undef;
                    607:        }
                    608:
                    609:        unless (lc($type) eq 'reg' || lc($type) eq 'pr') {
                    610:                warn "Invalid type '$type'!";
                    611:                return undef;
                    612:        }
                    613:
                    614:        if ($cir =~ /\D/) {
                    615:                warn "Invalid CIR '$cir'!";
                    616:                return undef;
                    617:        }
                    618:
                    619:        if ($mir =~ /\D/) {
                    620:                warn "Invalid MIR '$mir'!";
                    621:                return undef;
                    622:        }
                    623:
                    624:        my $new_mac = $mac;
                    625:        $new_mac =~ s/[^0-9A-Fa-f]//;
                    626:        unless (length $new_mac == 12) {
                    627:                warn "Invalid MAC '$mac'!";
                    628:                return undef;
                    629:        }
                    630:        $new_mac = join ' ', $new_mac =~ /../g;
                    631:
                    632:        my $string = 'sudb add ' .
                    633:                $suid . ' ' .
                    634:                $type . ' ' .
                    635:                $cir  . ' ' .
                    636:                $mir  . ' ' .
                    637:                $new_mac;
                    638:
                    639:
                    640:        return $self->cmd( String => $string, expect => $success );
                    641: }
                    642:
                    643: =pod
                    644:
                    645: =item sudb_delete
                    646:
                    647: Takes either 'all' or the  suid of the su to delete
                    648: and returns true on success or undef otherwise.
                    649:
                    650: You should save_sudb() after calling this, or your changes  will be lost
                    651: when the AP is rebooted.
                    652:
                    653: =cut
                    654:
                    655: sub sudb_delete
                    656: {
                    657:        my $self = shift;
                    658:        my $suid = shift;
                    659:
                    660:        if (lc($suid) ne 'all' || $suid =~ /\D/) {
                    661:                return undef;
                    662:        }
                    663:
                    664:        return $self->cmd( String => 'sudb delete ' . $suid, expect => $success );
                    665: }
                    666:
                    667: =pod
                    668:
                    669: =item sudb_modify
                    670:
                    671: Takes either the  suid of the su to delete
                    672: as well as what you are changing, either "cir, mir or su2su"
                    673: and returns true on success or undef otherwise.
                    674:
                    675: cir and mir also take a value to set the cir/mir to.
                    676:
                    677: su2su takes a group id parameter that is in hex.
                    678:
                    679: You should save_sudb() after calling this, or your changes  will be lost
                    680: when the AP is rebooted.
                    681:
                    682: =cut
                    683:
                    684: sub sudb_modify
                    685: {
                    686:        my $self  = shift;
                    687:        my $suid  = shift;
                    688:        my $opt   = shift;
                    689:        my $value = shift;
                    690:
                    691:        if ($suid =~ /\D/) {
                    692:                return undef;
                    693:        }
                    694:
                    695:        if (lc($opt) eq 'cir' or lc($opt) eq 'mir') {
                    696:                if ($value =~ /\D/) {
                    697:                        return undef;
                    698:                }
                    699:        } elsif (lc($opt) eq 'su2su') {
                    700:                if ($value =~ /[^0-9A-Za-f]/) {
                    701:                        return undef;
                    702:                }
                    703:        } else {
                    704:                return undef;
                    705:        }
                    706:
                    707:        my $string = 'sudb modify ' . $suid . ' ' . $opt . ' ' . $value;
                    708:
                    709:        return $self->cmd( String => $string, expect => $success );
                    710: }
                    711:
                    712: =pod
                    713:
                    714: =item enable_tftpd
                    715:
                    716: runs C<tftpd(args =E<gt> 'on')> and makes sure that Tftpd is now 'listen'ing
                    717:
                    718: =cut
                    719:
                    720: sub enable_tftpd
                    721: {
                    722:   my $self = shift;
                    723:
                    724:   my $vals = $self->tftpd( args => 'on' );
                    725:
                    726:   if ($vals->{'Tftpd'} eq 'listen') {
                    727:     return $vals;
                    728:   } else {
                    729:     return undef;
                    730:   }
                    731: }
                    732:
                    733: =pod
                    734:
                    735: =item disable_tftpd
                    736:
                    737: runs C<tftpd(args =E<gt> 'off')> and makes sure that Tftpd is now 'disabled'
                    738:
                    739: =cut
                    740:
                    741: sub disable_tftpd
                    742: {
                    743:   my $self = shift;
                    744:
                    745:   my $vals = $self->tftpd( args => 'off' );
                    746:
                    747:   if (ref $vals eq 'HASH' && $vals->{'Tftpd'} eq 'disabled') {
                    748:     return $vals;
                    749:   } else {
                    750:     return undef;
                    751:   }
                    752: }
                    753:
                    754: =pod
                    755:
                    756: =item cmd
                    757:
1.20      andrew    758: This does most of the work.  At the heart, it calls Net::Telnet::cmd()
                    759: but it also does some special stuff for Trango.
1.14      andrew    760:
                    761: Normally returns the last lines from from the command
                    762:
                    763: Also accepts these options:
                    764:
                    765: I<decode>
1.20      andrew    766: - if this is true, then it will send the output lines to _decode_lines()
                    767: and then returns the decoded output
1.14      andrew    768:
                    769: I<cmd_disconnects>
1.20      andrew    770: - if this is true, it then sets logged_in() to false, then it will
                    771: close() the connection and then sets is_connected() to false
1.14      andrew    772:
                    773: I<expect>
1.20      andrew    774: - if this is set (usually to 'Success.') it will check for that in the
                    775: last line of output and if it does not, will return undef because the
                    776: command probably failed
1.14      andrew    777:
                    778: I<args>
1.20      andrew    779: - a string containing the command line options that are passed to the
                    780: command
1.14      andrew    781:
                    782: =cut
                    783:
                    784: sub cmd
                    785: {
                    786:   my $self = shift;
                    787:
                    788:   my @valid_net_telnet_opts = qw(
                    789:     String
                    790:     Output
                    791:     Cmd_remove_mode
                    792:     Errmode
                    793:     Input_record_separator
                    794:     Ors
                    795:     Output_record_separator
                    796:     Prompt
                    797:     Rs
                    798:     Timeout
                    799:   );
                    800:
                    801:   my %cfg;
                    802:   if (@_ == 1) {
                    803:     $cfg{'String'} = shift;
                    804:   } elsif (@_ > 1) {
                    805:     %cfg = @_;
                    806:   }
                    807:
                    808:   $cfg{'Timeout'} ||= $self->Timeout;
                    809:
                    810:   unless ($cfg{'String'}) {
                    811:     #$! = "No command passed";
                    812:     #warn "No command passed\n";
                    813:     return undef;
                    814:   }
                    815:
                    816:   unless ($self->is_connected) {
                    817:     #$! = "Not connected";
                    818:     #warn "Not connected\n";
                    819:     return undef;
                    820:   }
                    821:
                    822:   unless ($self->logged_in) {
                    823:     #$! = "Not logged in";
                    824:     #warn "Not logged in\n";
                    825:     return undef;
                    826:   }
                    827:
                    828:
                    829:   my %cmd;
                    830:   foreach (@valid_net_telnet_opts) {
                    831:     if (exists $cfg{$_}) {
                    832:       $cmd{$_} = $cfg{$_};
                    833:     }
                    834:   }
                    835:   if ($cfg{'args'}) {
                    836:     $cmd{'String'} .= ' ' . $cfg{'args'};
                    837:   }
                    838:   my @lines;
                    839:   unless ($cfg{'no_prompt'}) {
                    840:     @lines = $self->SUPER::cmd(%cmd);
                    841:   } else {
                    842:     $self->print($cmd{'String'});
                    843:     @lines = $self->lastline;
                    844:   }
                    845:
                    846:   $self->last_lines(\@lines);
                    847:
                    848:   my $vals = 1;
                    849:   if ($PRIVATE{'Decode'} && $cfg{'decode'}) {
                    850:     if ($cfg{'decode'} eq 'each') {
                    851:       $vals = _decode_each_line(@lines);
                    852:     } elsif ($cfg{'decode'} eq 'sulog') {
                    853:       $vals = _decode_sulog(@lines);
                    854:     } elsif ($cfg{'decode'} eq 'maclist') {
                    855:       $vals = _decode_maclist(@lines);
                    856:     } else {
                    857:       $vals = _decode_lines(@lines);
                    858:     }
                    859:   }
                    860:
                    861:   $self->last_vals($vals);
                    862:
                    863:
                    864:   my $last = $self->lastline;
                    865:
                    866:   if ((not $cfg{'expect'}) || $last =~ /$cfg{'expect'}$/) {
                    867:     if ($cfg{'cmd_disconnects'}) {
                    868:       $self->logged_in(0);
                    869:       $self->close;
                    870:       $self->is_connected(0);
                    871:     }
                    872:
                    873:     if ($PRIVATE{'Decode'} && $cfg{'decode'}) {
                    874:       return $vals;
                    875:     } else {
                    876:       return @lines;
                    877:     }
                    878:   } else {
                    879:     #$! = "Error with command ($cfg{'string'}): $last";
                    880:     return undef;
                    881:   }
                    882: }
                    883:
                    884: #=item _decode_lines
                    885:
                    886: sub _decode_lines
                    887: {
                    888:   my @lines = @_;
                    889:
                    890:   my %conf;
                    891:
                    892:   my $key = '';
1.17      andrew    893:   my $val = undef;
1.14      andrew    894:   my $in_key = 0;
1.17      andrew    895:   my $in_val = 1;
1.14      andrew    896:
                    897:   foreach my $line (@lines) {
                    898:     next if $line =~ /$success$/;
                    899:
                    900:     my @chars = split //, $line;
                    901:
                    902:     my $last_key = '';
                    903:     foreach my $c (@chars) {
                    904:
                    905:       if ($c eq '[' || $c eq "\r" || $c eq "\n") {
                    906:         if ($c eq '[') {
                    907:           $in_key = 1;
                    908:           $in_val = 0;
                    909:         } else {
                    910:           $in_key = 0;
1.17      andrew    911:           $in_val = 1;
1.14      andrew    912:         }
                    913:
                    914:         if ($key) {
                    915:           $key =~ s/^\s+//;
                    916:           $key =~ s/\s+$//;
                    917:
1.17      andrew    918:           if (defined $val) {
                    919:             $val =~ s/^\s+//;
                    920:             $val =~ s/\s+$//;
                    921:           }
1.14      andrew    922:
                    923:           if ($key eq 'Checksum' && $last_key) {
                    924:             # Special case for these bastids.
                    925:             my $new = $last_key;
                    926:             $new =~ s/\s+\S+$//;
                    927:             $key = $new . " " . $key;
                    928:           }
                    929:
                    930:           $last_key = $key;
                    931:           $conf{$key} = $val;
                    932:           $key = '';
                    933:           $val = '';
                    934:         }
                    935:
                    936:       } elsif ($c eq ']') {
                    937:         $in_val = 1;
                    938:         $in_key = 0;
                    939:         $c = shift @chars;
                    940:
                    941:       } elsif ($in_key) {
                    942:         $key .= $c;
                    943:
                    944:       } elsif ($in_val) {
                    945:         $val .= $c;
                    946:       }
                    947:     }
                    948:   }
                    949:
                    950:   if (%conf) {
                    951:     return \%conf;
                    952:   } else {
1.17      andrew    953:     return $val;
1.14      andrew    954:   }
                    955: }
                    956:
                    957: #=item _decode_each_line
                    958:
                    959: sub _decode_each_line
                    960: {
                    961:   my @lines = @_;
                    962:   my @decoded;
                    963:   foreach my $line (@lines) {
                    964:     my $decoded = _decode_lines($line);
                    965:     push @decoded, $decoded if defined $decoded;
                    966:   }
                    967:   return \@decoded;
                    968: }
                    969:
                    970: #=item _decode_sulog
                    971:
                    972: sub _decode_sulog
                    973: {
                    974:   my @lines = @_;
                    975:   my @decoded;
                    976:   my $last_tm;
                    977:   foreach my $line (@lines) {
                    978:     my $decoded = _decode_lines($line);
                    979:
                    980:     if (defined $decoded) {
                    981:       if ($decoded->{'tm'}) {
                    982:         $last_tm = $decoded->{'tm'};
                    983:         next;
                    984:       } else {
                    985:         $decoded->{'tm'} = $last_tm;
                    986:       }
                    987:       next unless $last_tm;
                    988:
                    989:       push @decoded, $decoded if defined $decoded;
                    990:     }
                    991:   }
                    992:   return \@decoded;
                    993: }
                    994:
                    995: #=item _decode_maclist
                    996:
                    997: sub _decode_maclist
                    998: {
                    999:        my @lines = @_;
                   1000:        my @decoded;
                   1001:        my $total_entries = 0;
                   1002:        my $current_tm = 0;
                   1003:        foreach my $line (@lines) {
                   1004:                $line =~ s/\r?\n$//;
                   1005:                my ($mac, $loc, $tm) = $line =~ /
                   1006:                        ([0-9a-fA-F ]{17})\s+
                   1007:                        (.*)\s+
                   1008:                        tm\s+
                   1009:                        (\d+)
                   1010:                /x;
                   1011:
                   1012:                if ($mac) {
                   1013:                        $mac =~ s/\s+//g;
                   1014:                        $loc =~ s/^\s+//;
                   1015:                        $loc =~ s/\s+$//;
                   1016:
                   1017:                        my $suid = undef;
                   1018:                        if ($loc =~ /suid\s+=\s+(\d+)/) {
                   1019:                                $suid = $1;
                   1020:                                $loc = undef;
                   1021:                        }
                   1022:
                   1023:                        push @decoded, {
                   1024:                                mac  => $mac,
                   1025:                                loc  => $loc,
                   1026:                                tm   => $tm,
                   1027:                                suid => $suid,
                   1028:                        };
                   1029:                } elsif ($line =~ /(\d+)\s+entries/) {
                   1030:                        $total_entries = $1;
                   1031:                } elsif ($line =~ /current tm = (\d+)\s+sec/) {
                   1032:                        $current_tm = $1
                   1033:                }
                   1034:        }
1.15      andrew   1035:
                   1036:        map { $_->{'cur_tm'} = $current_tm } @decoded;
                   1037:
1.14      andrew   1038:        if (scalar @decoded == $total_entries) {
                   1039:                return \@decoded;
                   1040:        } else {
                   1041:                # XXX we should have a way to set last error, not sure why we don't
                   1042:                return undef;
                   1043:        }
                   1044: }
                   1045:
                   1046: 1;
                   1047: __END__
                   1048:
                   1049: =back
                   1050:
                   1051: =head1 SEE ALSO
                   1052:
1.20      andrew   1053: Trango Documentation -
                   1054: http://www.trangobroadband.com/support/product_docs.htm
1.14      andrew   1055:
                   1056: L<Net::Telnet>
                   1057:
                   1058: =head1 TODO
                   1059:
1.20      andrew   1060: There are still a lot of commands that are not accessed directly.  If
                   1061: you call them (as cmd("command + args") or whatever) and it works,
                   1062: please send me examples that work and I will try to get it incorporated
                   1063: into the next version of the script.
                   1064:
                   1065: I also want to be able to parse the different types of output from
                   1066: commands like su, sudb all and anything else that would be better
                   1067: available as a perl datastructure.
1.14      andrew   1068:
                   1069: =head1 AUTHOR
                   1070:
                   1071: Andrew Fresh E<lt>andrew@rraz.netE<gt>
                   1072:
                   1073: =head1 COPYRIGHT AND LICENSE
                   1074:
                   1075: Copyright (C) 2005 by Andrew Fresh
                   1076:
                   1077: This library is free software; you can redistribute it and/or modify
                   1078: it under the same terms as Perl itself, either Perl version 5.8.7 or,
                   1079: at your option, any later version of Perl 5 you may have available.
                   1080:
                   1081:
                   1082: =cut

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>