[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.20

1.14      andrew      1: package Net::Telnet::Trango;
1.20    ! andrew      2: # $RedRiver: Trango.pm,v 1.19 2006/09/12 02:16:43 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.14      andrew    317:   # eth r, w and reset???
                    318:   #su password???
                    319:   #_bootloader
                    320:   #temp
                    321:   #heater
                    322: );
                    323:
                    324: my %ALIASES = (
                    325:   bye     => 'exit',
                    326:   restart => 'reboot',
                    327: );
                    328:
                    329: my %ACCESS = map { $_ => 1 } qw(
                    330:   firmware_version
                    331:   host_type
                    332:   Host
                    333:   is_connected
                    334:   logged_in
                    335:   login_banner
                    336:   Timeout
                    337:   last_lines
                    338:   last_vals
                    339: );
                    340:
                    341: sub AUTOLOAD
                    342: {
                    343:   my $self = shift;
                    344:
                    345:   my ($method) = (our $AUTOLOAD) =~ /^.*::(\w+)$/
                    346:     or die "Weird: $AUTOLOAD";
                    347:
                    348:   if (exists $ALIASES{$method}) {
                    349:     $method = $ALIASES{$method};
                    350:     return $self->$method(@_);
                    351:   }
                    352:
                    353:   if (exists $COMMANDS{$method}) {
                    354:     $COMMANDS{$method}{'String'} ||= $method;
1.17      andrew    355:     $COMMANDS{$method}{'args'} .= ' ' . shift if (@_ == 1);
1.14      andrew    356:     return $self->cmd(%{ $COMMANDS{$method} }, @_);
                    357:   }
                    358:
                    359:   if (exists $ACCESS{$method}) {
                    360:     my $prev = $PRIVATE{$method};
                    361:     ($PRIVATE{$method}) = @_ if @_;
                    362:     return $prev;
                    363:   }
                    364:
                    365:   $method = "SUPER::$method";
                    366:   return $self->$method(@_);
                    367: }
                    368:
                    369: =pod
                    370:
                    371: =item open
                    372:
1.20    ! andrew    373: Calls Net::Telnet::open() then makes sure you get a password prompt so
        !           374: you are ready to login() and parses the login banner so you can get
        !           375: host_type() and firmware_version()
1.14      andrew    376:
                    377: =cut
                    378:
                    379: sub open
                    380: {
                    381:   my $self = shift;
                    382:
                    383:   unless ( $self->SUPER::open(@_) ) {
                    384:     #$! = "Couldn't connect to " . $self->Host . ":  $!";
                    385:     return undef;
                    386:   }
                    387:
                    388:   ## Get to login prompt
                    389:   unless ($self->waitfor(
                    390:       -match => '/password: ?$/i',
                    391:       -errmode => "return",
                    392:     ) ) {
                    393:     #$! = "problem connecting to host (" . $self->Host . "): " .
                    394:     #    $self->lastline;
                    395:     return undef;
                    396:   }
                    397:
                    398:   $self->parse_login_banner($self->lastline);
                    399:
                    400:   $self->is_connected(1);
                    401:
                    402:   return $self->is_connected;
                    403: }
                    404:
                    405: =pod
                    406:
                    407: =item login
                    408:
1.20    ! andrew    409: Calls open() if not already connected, then sends the password and sets
        !           410: logged_in() if successful
1.14      andrew    411:
                    412: =cut
                    413:
                    414: sub login
                    415: {
                    416:   my $self = shift;
                    417:
                    418:   unless ($self->is_connected) {
                    419:     $self->open or return undef;
                    420:   }
                    421:
                    422:   my $password = shift;
                    423:
                    424:   $self->print($password);
                    425:   unless ($self->waitfor(
                    426:     -match => $self->prompt,
                    427:     -errmode => "return",
                    428:   ) ) {
                    429:     #$! = "login ($self->Host) failed: " . $self->lastline;
                    430:     return undef;
                    431:   }
                    432:
                    433:   $self->logged_in(1);
                    434:
                    435:   return $self->logged_in;
                    436: }
                    437:
                    438: =pod
                    439:
                    440: =item parse_login_banner
                    441:
1.20    ! andrew    442: Takes a login banner (what you get when you first connect to the Trango)
        !           443: or reads what is already in login_banner() then parses it and sets
        !           444: host_type() and firmware_version() as well as login_banner()
1.14      andrew    445:
                    446: =cut
                    447:
                    448: sub parse_login_banner
                    449: {
                    450:   my $self = shift;
                    451:
                    452:   if (@_) {
                    453:     $self->login_banner(@_);
                    454:   }
                    455:
                    456:   my $banner = $self->login_banner;
                    457:
                    458:   my ($type, $ver) = $banner =~
                    459:     /Welcome to Trango Broadband Wireless (\S+)[\s-]+(.+)$/i;
                    460:
                    461:   $self->login_banner($banner);
                    462:   $self->host_type($type);
                    463:   $self->firmware_version($ver);
                    464:
                    465:   return 1;
                    466: }
                    467:
                    468: =pod
                    469:
                    470: =item su_password
                    471:
                    472: C<su_password('all'|suid, 'new_password')>
                    473:
                    474: =cut
                    475:
                    476: sub su_password
                    477: {
                    478:   my $self     = shift;
                    479:   my $su       = shift || '!';
                    480:   my $new_pass = shift || '';
                    481:
                    482:   unless (defined $su) {
                    483:     warn "No su passed!"
                    484:     #return undef;
                    485:   }
                    486:
                    487:   unless (defined $new_pass) {
                    488:     warn "No new password!"
                    489:     #return undef;
                    490:   }
                    491:
                    492:   return $self->cmd(String => 'su password ' .
                    493:                      $su . ' ' .
                    494:                      $new_pass . ' ' .
                    495:                      $new_pass,
                    496:                      expect => $success,
                    497:                     );
                    498: }
                    499:
                    500: =pod
                    501:
                    502: =item su_ipconfig
                    503:
                    504: C<su_ipconfig( 'suid', 'new_ip', 'new_subnet', 'new_gateway' )>
                    505:
                    506: =cut
                    507:
                    508: sub su_ipconfig
                    509: {
                    510:        my $self        = shift;
                    511:
                    512:        my $suid        = shift;
                    513:        my $new_ip      = shift;
                    514:        my $new_subnet  = shift;
                    515:        my $new_gateway = shift;
                    516:
                    517:        return undef unless $suid =~ /^\d+$/;
                    518:        return undef unless $new_ip;
                    519:        return undef unless $new_subnet;
                    520:        return undef unless $new_gateway;
                    521:
                    522:        # su ipconfig <suid> <new ip> <new subnet> <new gateway>
                    523:        return $self->cmd(String => 'su ipconfig ' .
                    524:                      $suid       . ' ' .
                    525:                      $new_ip     . ' ' .
                    526:                      $new_subnet . ' ' .
                    527:                      $new_gateway,
                    528:                      expect => $success,
                    529:                     );
                    530: }
                    531:
                    532: =pod
                    533:
                    534: =item sudb_view
                    535:
                    536: returns a reference to an array of hashes each containing:
                    537:
                    538:   suid
                    539:   type
                    540:   cir
                    541:   mir
                    542:   mac
                    543:
                    544: =cut
                    545:
                    546: sub sudb_view
                    547: {
                    548:   my $self = shift;
                    549:
                    550:   my @lines = $self->cmd( String => 'sudb view', expect => $success );
                    551:
                    552:   return undef unless @lines;
                    553:
                    554:   unless ($PRIVATE{'Decode'}) {
                    555:     return @lines;
                    556:   }
                    557:
                    558:   my @sus;
                    559:   foreach (@lines) {
                    560:     next unless $_;
                    561:     if (/^\[(\d+)\]\s+(\d+)\s+(\d+)\s+(\d+)\s+([0-9A-Fa-f\s]+)$/) {
                    562:       my %s = (
                    563:         suid => $1,
                    564:         type => $2,
                    565:         cir  => $3,
                    566:         mir  => $4,
                    567:         mac  => $5,
                    568:       );
                    569:
                    570:          $s{'mac'} =~ s/\s//g;
                    571:          $s{'mac'} = uc($s{'mac'});
                    572:
                    573:       push @sus, \%s;
                    574:     }
                    575:   }
                    576:
                    577:   return \@sus;
                    578: }
                    579:
                    580: =pod
                    581:
                    582: =item sudb_add
                    583:
                    584: Takes the following paramaters
                    585:
                    586:        suid : numeric,
                    587:        type : (reg|pr)
                    588:        cir  : numeric,
                    589:        mir  : numeric,
                    590:        mac  : Almost any format, it will be reformatted,
                    591:
                    592: and returns true on success or undef otherwise.
                    593:
                    594: You should save_sudb() after calling this, or your changes  will be lost
                    595: when the AP is rebooted.
                    596:
                    597: =cut
                    598:
                    599: sub sudb_add
                    600: {
                    601:        my $self = shift;
                    602:        my $suid = shift;
                    603:        my $type = shift;
                    604:        my $cir  = shift;
                    605:        my $mir  = shift;
                    606:        my $mac  = shift;
                    607:
                    608:        if ($suid =~ /\D/) {
                    609:                return undef;
                    610:        }
                    611:
                    612:        unless (lc($type) eq 'reg' || lc($type) eq 'pr') {
                    613:                warn "Invalid type '$type'!";
                    614:                return undef;
                    615:        }
                    616:
                    617:        if ($cir =~ /\D/) {
                    618:                warn "Invalid CIR '$cir'!";
                    619:                return undef;
                    620:        }
                    621:
                    622:        if ($mir =~ /\D/) {
                    623:                warn "Invalid MIR '$mir'!";
                    624:                return undef;
                    625:        }
                    626:
                    627:        my $new_mac = $mac;
                    628:        $new_mac =~ s/[^0-9A-Fa-f]//;
                    629:        unless (length $new_mac == 12) {
                    630:                warn "Invalid MAC '$mac'!";
                    631:                return undef;
                    632:        }
                    633:        $new_mac = join ' ', $new_mac =~ /../g;
                    634:
                    635:        my $string = 'sudb add ' .
                    636:                $suid . ' ' .
                    637:                $type . ' ' .
                    638:                $cir  . ' ' .
                    639:                $mir  . ' ' .
                    640:                $new_mac;
                    641:
                    642:
                    643:        return $self->cmd( String => $string, expect => $success );
                    644: }
                    645:
                    646: =pod
                    647:
                    648: =item sudb_delete
                    649:
                    650: Takes either 'all' or the  suid of the su to delete
                    651: and returns true on success or undef otherwise.
                    652:
                    653: You should save_sudb() after calling this, or your changes  will be lost
                    654: when the AP is rebooted.
                    655:
                    656: =cut
                    657:
                    658: sub sudb_delete
                    659: {
                    660:        my $self = shift;
                    661:        my $suid = shift;
                    662:
                    663:        if (lc($suid) ne 'all' || $suid =~ /\D/) {
                    664:                return undef;
                    665:        }
                    666:
                    667:        return $self->cmd( String => 'sudb delete ' . $suid, expect => $success );
                    668: }
                    669:
                    670: =pod
                    671:
                    672: =item sudb_modify
                    673:
                    674: Takes either the  suid of the su to delete
                    675: as well as what you are changing, either "cir, mir or su2su"
                    676: and returns true on success or undef otherwise.
                    677:
                    678: cir and mir also take a value to set the cir/mir to.
                    679:
                    680: su2su takes a group id parameter that is in hex.
                    681:
                    682: You should save_sudb() after calling this, or your changes  will be lost
                    683: when the AP is rebooted.
                    684:
                    685: =cut
                    686:
                    687: sub sudb_modify
                    688: {
                    689:        my $self  = shift;
                    690:        my $suid  = shift;
                    691:        my $opt   = shift;
                    692:        my $value = shift;
                    693:
                    694:        if ($suid =~ /\D/) {
                    695:                return undef;
                    696:        }
                    697:
                    698:        if (lc($opt) eq 'cir' or lc($opt) eq 'mir') {
                    699:                if ($value =~ /\D/) {
                    700:                        return undef;
                    701:                }
                    702:        } elsif (lc($opt) eq 'su2su') {
                    703:                if ($value =~ /[^0-9A-Za-f]/) {
                    704:                        return undef;
                    705:                }
                    706:        } else {
                    707:                return undef;
                    708:        }
                    709:
                    710:        my $string = 'sudb modify ' . $suid . ' ' . $opt . ' ' . $value;
                    711:
                    712:        return $self->cmd( String => $string, expect => $success );
                    713: }
                    714:
                    715: =pod
                    716:
                    717: =item enable_tftpd
                    718:
                    719: runs C<tftpd(args =E<gt> 'on')> and makes sure that Tftpd is now 'listen'ing
                    720:
                    721: =cut
                    722:
                    723: sub enable_tftpd
                    724: {
                    725:   my $self = shift;
                    726:
                    727:   my $vals = $self->tftpd( args => 'on' );
                    728:
                    729:   if ($vals->{'Tftpd'} eq 'listen') {
                    730:     return $vals;
                    731:   } else {
                    732:     return undef;
                    733:   }
                    734: }
                    735:
                    736: =pod
                    737:
                    738: =item disable_tftpd
                    739:
                    740: runs C<tftpd(args =E<gt> 'off')> and makes sure that Tftpd is now 'disabled'
                    741:
                    742: =cut
                    743:
                    744: sub disable_tftpd
                    745: {
                    746:   my $self = shift;
                    747:
                    748:   my $vals = $self->tftpd( args => 'off' );
                    749:
                    750:   if (ref $vals eq 'HASH' && $vals->{'Tftpd'} eq 'disabled') {
                    751:     return $vals;
                    752:   } else {
                    753:     return undef;
                    754:   }
                    755: }
                    756:
                    757: =pod
                    758:
                    759: =item cmd
                    760:
1.20    ! andrew    761: This does most of the work.  At the heart, it calls Net::Telnet::cmd()
        !           762: but it also does some special stuff for Trango.
1.14      andrew    763:
                    764: Normally returns the last lines from from the command
                    765:
                    766: Also accepts these options:
                    767:
                    768: I<decode>
1.20    ! andrew    769: - if this is true, then it will send the output lines to _decode_lines()
        !           770: and then returns the decoded output
1.14      andrew    771:
                    772: I<cmd_disconnects>
1.20    ! andrew    773: - if this is true, it then sets logged_in() to false, then it will
        !           774: close() the connection and then sets is_connected() to false
1.14      andrew    775:
                    776: I<expect>
1.20    ! andrew    777: - if this is set (usually to 'Success.') it will check for that in the
        !           778: last line of output and if it does not, will return undef because the
        !           779: command probably failed
1.14      andrew    780:
                    781: I<args>
1.20    ! andrew    782: - a string containing the command line options that are passed to the
        !           783: command
1.14      andrew    784:
                    785: =cut
                    786:
                    787: sub cmd
                    788: {
                    789:   my $self = shift;
                    790:
                    791:   my @valid_net_telnet_opts = qw(
                    792:     String
                    793:     Output
                    794:     Cmd_remove_mode
                    795:     Errmode
                    796:     Input_record_separator
                    797:     Ors
                    798:     Output_record_separator
                    799:     Prompt
                    800:     Rs
                    801:     Timeout
                    802:   );
                    803:
                    804:   my %cfg;
                    805:   if (@_ == 1) {
                    806:     $cfg{'String'} = shift;
                    807:   } elsif (@_ > 1) {
                    808:     %cfg = @_;
                    809:   }
                    810:
                    811:   $cfg{'Timeout'} ||= $self->Timeout;
                    812:
                    813:   unless ($cfg{'String'}) {
                    814:     #$! = "No command passed";
                    815:     #warn "No command passed\n";
                    816:     return undef;
                    817:   }
                    818:
                    819:   unless ($self->is_connected) {
                    820:     #$! = "Not connected";
                    821:     #warn "Not connected\n";
                    822:     return undef;
                    823:   }
                    824:
                    825:   unless ($self->logged_in) {
                    826:     #$! = "Not logged in";
                    827:     #warn "Not logged in\n";
                    828:     return undef;
                    829:   }
                    830:
                    831:
                    832:   my %cmd;
                    833:   foreach (@valid_net_telnet_opts) {
                    834:     if (exists $cfg{$_}) {
                    835:       $cmd{$_} = $cfg{$_};
                    836:     }
                    837:   }
                    838:   if ($cfg{'args'}) {
                    839:     $cmd{'String'} .= ' ' . $cfg{'args'};
                    840:   }
                    841:   my @lines;
                    842:   unless ($cfg{'no_prompt'}) {
                    843:     @lines = $self->SUPER::cmd(%cmd);
                    844:   } else {
                    845:     $self->print($cmd{'String'});
                    846:     @lines = $self->lastline;
                    847:   }
                    848:
                    849:   $self->last_lines(\@lines);
                    850:
                    851:   my $vals = 1;
                    852:   if ($PRIVATE{'Decode'} && $cfg{'decode'}) {
                    853:     if ($cfg{'decode'} eq 'each') {
                    854:       $vals = _decode_each_line(@lines);
                    855:     } elsif ($cfg{'decode'} eq 'sulog') {
                    856:       $vals = _decode_sulog(@lines);
                    857:     } elsif ($cfg{'decode'} eq 'maclist') {
                    858:       $vals = _decode_maclist(@lines);
                    859:     } else {
                    860:       $vals = _decode_lines(@lines);
                    861:     }
                    862:   }
                    863:
                    864:   $self->last_vals($vals);
                    865:
                    866:
                    867:   my $last = $self->lastline;
                    868:
                    869:   if ((not $cfg{'expect'}) || $last =~ /$cfg{'expect'}$/) {
                    870:     if ($cfg{'cmd_disconnects'}) {
                    871:       $self->logged_in(0);
                    872:       $self->close;
                    873:       $self->is_connected(0);
                    874:     }
                    875:
                    876:     if ($PRIVATE{'Decode'} && $cfg{'decode'}) {
                    877:       return $vals;
                    878:     } else {
                    879:       return @lines;
                    880:     }
                    881:   } else {
                    882:     #$! = "Error with command ($cfg{'string'}): $last";
                    883:     return undef;
                    884:   }
                    885: }
                    886:
                    887: #=item _decode_lines
                    888:
                    889: sub _decode_lines
                    890: {
                    891:   my @lines = @_;
                    892:
                    893:   my %conf;
                    894:
                    895:   my $key = '';
1.17      andrew    896:   my $val = undef;
1.14      andrew    897:   my $in_key = 0;
1.17      andrew    898:   my $in_val = 1;
1.14      andrew    899:
                    900:   foreach my $line (@lines) {
                    901:     next if $line =~ /$success$/;
                    902:
                    903:     my @chars = split //, $line;
                    904:
                    905:     my $last_key = '';
                    906:     foreach my $c (@chars) {
                    907:
                    908:       if ($c eq '[' || $c eq "\r" || $c eq "\n") {
                    909:         if ($c eq '[') {
                    910:           $in_key = 1;
                    911:           $in_val = 0;
                    912:         } else {
                    913:           $in_key = 0;
1.17      andrew    914:           $in_val = 1;
1.14      andrew    915:         }
                    916:
                    917:         if ($key) {
                    918:           $key =~ s/^\s+//;
                    919:           $key =~ s/\s+$//;
                    920:
1.17      andrew    921:           if (defined $val) {
                    922:             $val =~ s/^\s+//;
                    923:             $val =~ s/\s+$//;
                    924:           }
1.14      andrew    925:
                    926:           if ($key eq 'Checksum' && $last_key) {
                    927:             # Special case for these bastids.
                    928:             my $new = $last_key;
                    929:             $new =~ s/\s+\S+$//;
                    930:             $key = $new . " " . $key;
                    931:           }
                    932:
                    933:           $last_key = $key;
                    934:           $conf{$key} = $val;
                    935:           $key = '';
                    936:           $val = '';
                    937:         }
                    938:
                    939:       } elsif ($c eq ']') {
                    940:         $in_val = 1;
                    941:         $in_key = 0;
                    942:         $c = shift @chars;
                    943:
                    944:       } elsif ($in_key) {
                    945:         $key .= $c;
                    946:
                    947:       } elsif ($in_val) {
                    948:         $val .= $c;
                    949:       }
                    950:     }
                    951:   }
                    952:
                    953:   if (%conf) {
                    954:     return \%conf;
                    955:   } else {
1.17      andrew    956:     return $val;
1.14      andrew    957:   }
                    958: }
                    959:
                    960: #=item _decode_each_line
                    961:
                    962: sub _decode_each_line
                    963: {
                    964:   my @lines = @_;
                    965:   my @decoded;
                    966:   foreach my $line (@lines) {
                    967:     my $decoded = _decode_lines($line);
                    968:     push @decoded, $decoded if defined $decoded;
                    969:   }
                    970:   return \@decoded;
                    971: }
                    972:
                    973: #=item _decode_sulog
                    974:
                    975: sub _decode_sulog
                    976: {
                    977:   my @lines = @_;
                    978:   my @decoded;
                    979:   my $last_tm;
                    980:   foreach my $line (@lines) {
                    981:     my $decoded = _decode_lines($line);
                    982:
                    983:     if (defined $decoded) {
                    984:       if ($decoded->{'tm'}) {
                    985:         $last_tm = $decoded->{'tm'};
                    986:         next;
                    987:       } else {
                    988:         $decoded->{'tm'} = $last_tm;
                    989:       }
                    990:       next unless $last_tm;
                    991:
                    992:       push @decoded, $decoded if defined $decoded;
                    993:     }
                    994:   }
                    995:   return \@decoded;
                    996: }
                    997:
                    998: #=item _decode_maclist
                    999:
                   1000: sub _decode_maclist
                   1001: {
                   1002:        my @lines = @_;
                   1003:        my @decoded;
                   1004:        my $total_entries = 0;
                   1005:        my $current_tm = 0;
                   1006:        foreach my $line (@lines) {
                   1007:                $line =~ s/\r?\n$//;
                   1008:                my ($mac, $loc, $tm) = $line =~ /
                   1009:                        ([0-9a-fA-F ]{17})\s+
                   1010:                        (.*)\s+
                   1011:                        tm\s+
                   1012:                        (\d+)
                   1013:                /x;
                   1014:
                   1015:                if ($mac) {
                   1016:                        $mac =~ s/\s+//g;
                   1017:                        $loc =~ s/^\s+//;
                   1018:                        $loc =~ s/\s+$//;
                   1019:
                   1020:                        my $suid = undef;
                   1021:                        if ($loc =~ /suid\s+=\s+(\d+)/) {
                   1022:                                $suid = $1;
                   1023:                                $loc = undef;
                   1024:                        }
                   1025:
                   1026:                        push @decoded, {
                   1027:                                mac  => $mac,
                   1028:                                loc  => $loc,
                   1029:                                tm   => $tm,
                   1030:                                suid => $suid,
                   1031:                        };
                   1032:                } elsif ($line =~ /(\d+)\s+entries/) {
                   1033:                        $total_entries = $1;
                   1034:                } elsif ($line =~ /current tm = (\d+)\s+sec/) {
                   1035:                        $current_tm = $1
                   1036:                }
                   1037:        }
1.15      andrew   1038:
                   1039:        map { $_->{'cur_tm'} = $current_tm } @decoded;
                   1040:
1.14      andrew   1041:        if (scalar @decoded == $total_entries) {
                   1042:                return \@decoded;
                   1043:        } else {
                   1044:                # XXX we should have a way to set last error, not sure why we don't
                   1045:                return undef;
                   1046:        }
                   1047: }
                   1048:
                   1049: 1;
                   1050: __END__
                   1051:
                   1052: =back
                   1053:
                   1054: =head1 SEE ALSO
                   1055:
1.20    ! andrew   1056: Trango Documentation -
        !          1057: http://www.trangobroadband.com/support/product_docs.htm
1.14      andrew   1058:
                   1059: L<Net::Telnet>
                   1060:
                   1061: =head1 TODO
                   1062:
1.20    ! andrew   1063: There are still a lot of commands that are not accessed directly.  If
        !          1064: you call them (as cmd("command + args") or whatever) and it works,
        !          1065: please send me examples that work and I will try to get it incorporated
        !          1066: into the next version of the script.
        !          1067:
        !          1068: I also want to be able to parse the different types of output from
        !          1069: commands like su, sudb all and anything else that would be better
        !          1070: available as a perl datastructure.
1.14      andrew   1071:
                   1072: =head1 AUTHOR
                   1073:
                   1074: Andrew Fresh E<lt>andrew@rraz.netE<gt>
                   1075:
                   1076: =head1 COPYRIGHT AND LICENSE
                   1077:
                   1078: Copyright (C) 2005 by Andrew Fresh
                   1079:
                   1080: This library is free software; you can redistribute it and/or modify
                   1081: it under the same terms as Perl itself, either Perl version 5.8.7 or,
                   1082: at your option, any later version of Perl 5 you may have available.
                   1083:
                   1084:
                   1085: =cut

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