Annotation of trango/Net-Telnet-Trango/scripts/update_trango.pl, Revision 1.8
1.1 andrew 1: #!/usr/bin/perl
1.8 ! andrew 2: # $RedRiver: update_trango.pl,v 1.7 2005/11/17 20:09:45 andrew Exp $
1.1 andrew 3: ########################################################################
4: # update_trango.pl *** Updates trango foxes with a new firmware
5: #
6: # 2005.11.15 #*#*# andrew fresh <andrew@mad-techies.org>
7: ########################################################################
8: use strict;
9: use warnings;
10:
11: use Net::Telnet;
1.2 andrew 12: use Net::TFTP;
1.4 andrew 13: use Fcntl ':flock'; # import LOCK_* constants
1.3 andrew 14: #use YAML;
1.8 ! andrew 15: use constant LOG_SAVE => 1;
1.2 andrew 16:
17:
1.4 andrew 18: my $config_file = shift || 'update_trango.conf';
1.8 ! andrew 19: my $max_tries = 1;
! 20:
! 21:
! 22:
! 23:
! 24:
1.2 andrew 25:
26:
1.4 andrew 27: my $log_file = GetLogName('UT');
28: my $MYLOG; # file handle for logging so we don't have to close and open it all the time
29: END {
30: if ($MYLOG) {
31: mylog("Closing log ($log_file)");
32: close $MYLOG;
33: }
34: }
35:
36:
1.8 ! andrew 37: mylog("Reading config file '$config_file'", LOG_SAVE);
! 38: my $conf = read_conf($config_file);
! 39:
! 40: mylog(" Hardware Type: $conf->{'type'}", LOG_SAVE);
! 41: mylog(" File Name: $conf->{'file_name'}", LOG_SAVE);
! 42: mylog(" File Size: $conf->{'file_size'}", LOG_SAVE);
! 43: mylog(" File Checksum: $conf->{'file_cksum'}", LOG_SAVE);
! 44: mylog(" FW Version: $conf->{'ver'}", LOG_SAVE);
! 45: mylog(" FW Checksum: $conf->{'cksum'}", LOG_SAVE);
! 46: mylog("", LOG_SAVE);
1.4 andrew 47:
1.2 andrew 48:
49:
50:
51:
52:
1.3 andrew 53: foreach my $fox (@{ $conf->{'ips'} }) {
1.5 andrew 54: mylog("Updating: $fox", LOG_SAVE);
1.2 andrew 55:
56: ## Connect and login.
57: my $host = new Net::Telnet (Timeout => 5,
58: Prompt => '/#> *$/');
1.4 andrew 59: mylog("Connecting to $fox");
1.6 andrew 60: unless ( $host->open( Host => $fox, Errmode => 'return') ) {
61: mylog("Couldn't connect to $fox. Connection timed out.", LOG_SAVE);
62: next;
63: }
1.2 andrew 64: $host->dump_log('dump.log');
65:
66: ## Login to remote host.
1.6 andrew 67: unless ($host->waitfor(
1.2 andrew 68: -match => '/password: ?$/i',
69: -errmode => "return",
1.6 andrew 70: ) ) {
71: mylog("problem connecting to host ($fox): " . $host->lastline, LOG_SAVE);
72: next;
73: }
1.3 andrew 74:
75: my $login_banner = $host->lastline;
76: my ($type, $version) = $login_banner =~
77: /Welcome to Trango Broadband Wireless (\w+)-(.+)$/i;
78:
79: if ($type ne $conf->{'type'}) {
1.5 andrew 80: mylog("Wrong type of unit ('$type' should be '$conf->{'type'}')", LOG_SAVE);
1.3 andrew 81: $host->close;
82: next;
83: }
84:
85: if ($version eq $conf->{'ver'}) {
1.5 andrew 86: mylog("Already up to date with firmware version '$version'", LOG_SAVE);
1.3 andrew 87: $host->close;
88: next;
89: }
90:
1.4 andrew 91: mylog("Logging in");
1.3 andrew 92: $host->print($conf->{'password'});
1.5 andrew 93: unless ($host->waitfor(
1.2 andrew 94: -match => $host->prompt,
95: -errmode => "return",
1.5 andrew 96: ) ) {
97: mylog("login ($fox) failed: " . $host->lastline);
98: next;
99: }
1.2 andrew 100:
1.3 andrew 101:
1.4 andrew 102: mylog("Sending commands");
1.2 andrew 103: ## Send commands
1.3 andrew 104: if ( upload($host, $fox, $conf->{'file_name'}) ) {
1.4 andrew 105: mylog("Rebooting");
1.3 andrew 106: $host->print("reboot\n");
107: $host->getline;
1.2 andrew 108: } else {
1.4 andrew 109: mylog("Exiting");
1.3 andrew 110: $host->print("exit\n");
111: $host->getline;
1.2 andrew 112: }
113: $host->close;
1.5 andrew 114: mylog("", LOG_SAVE);
1.2 andrew 115: }
116:
117: sub upload
118: {
119: my $host = shift;
1.3 andrew 120: my $fox = shift;
1.2 andrew 121: my $file = shift;
1.3 andrew 122:
1.4 andrew 123: mylog("Getting current version");
1.2 andrew 124: my $ver = get_ver($host);
125:
126: if (
1.3 andrew 127: $ver->{'Firmware Version'} eq $conf->{'ver'} &&
128: $ver->{'Firmware Checksum'} eq $conf->{'cksum'}
1.2 andrew 129: ) {
1.5 andrew 130: mylog("Already updated!", LOG_SAVE);
1.2 andrew 131: return 1;
132: }
133:
134: my $try = 0;
135: while (1) {
1.3 andrew 136: if ($try >= $max_tries) {
1.4 andrew 137: mylog("Couldn't update in $max_tries tries!");
1.3 andrew 138: return undef;
139: }
1.2 andrew 140: $try++;
141:
1.3 andrew 142: #sysinfo($host);
143:
1.4 andrew 144: mylog("Enabling TFTPd");
1.2 andrew 145: enable_tftpd($host) || die "Couldn't enable tftpd";
146:
1.5 andrew 147: mylog("Uploading file ($file)");
1.2 andrew 148: # use tftp to push the file up
1.3 andrew 149: my $tftp = Net::TFTP->new($fox, Mode => 'octet');
150:
151: $tftp->put($file, $file)
152: or die "Error uploading: " . $tftp->error;
1.2 andrew 153:
154: # waitfor some sort of output
155: # make sure it says 'Success.' otherwise error
1.3 andrew 156: #print "LAST: " . $host->lastline;
157: #my @lines = $host->getlines;
158: #print Dump \@lines;
1.2 andrew 159:
1.5 andrew 160: mylog("Checking upload ($conf->{'file_cksum'})");
1.2 andrew 161: my $results = check_tftpd($host);
162: # check the 'File Length' against ???
1.3 andrew 163: if ( $results->{'File Checksum'} ne $conf->{'file_cksum'}) {
1.5 andrew 164: mylog("File checksum does not match config file!", LOG_SAVE);
1.3 andrew 165: next;
1.5 andrew 166: }
167: mylog("File checksum ($results->{'File Checksum'}) " .
168: "matches ($conf->{'file_cksum'})");
1.3 andrew 169:
170: if ($results->{'File Length'} !~ /^$conf->{'file_size'} bytes/) {
1.5 andrew 171: mylog("File length does not match config file!", LOG_SAVE);
1.3 andrew 172: next;
173: }
1.5 andrew 174: mylog("File length ($results->{'File Length'}) " .
175: "matches ($conf->{'file_size'})");
1.2 andrew 176:
1.3 andrew 177: if ( uc($results->{'File Name'}) ne uc($conf->{'file_name'}) ) {
1.5 andrew 178: mylog("File name does not match config file!", LOG_SAVE);
1.3 andrew 179: next;
180: }
1.5 andrew 181: mylog("File name ($results->{'File Name'}) " .
182: "matches ($conf->{'file_name'})");
1.2 andrew 183:
1.5 andrew 184: mylog("Updating flash (new checksum '$conf->{'cksum'}')");
1.3 andrew 185: $results = updateflash(
186: $host, $ver->{'Firmware Checksum'}, $conf->{'cksum'}
187: ) or die "Couldn't update flash: " . $host->lastline;
188: unless (
189: defined $results->{'Checksum'} &&
190: $results->{'Checksum'} eq $conf->{'cksum'}
191: ) {
1.5 andrew 192: mylog("Saved checksum does not match config file!", LOG_SAVE);
1.3 andrew 193: next;
1.2 andrew 194: }
1.5 andrew 195: mylog("Uploaded checksum ($results->{'Checksum'}) " .
196: "matches ($conf->{'cksum'})");
1.3 andrew 197:
1.5 andrew 198: mylog("Successfully updated!", LOG_SAVE);
1.3 andrew 199: return 1;
1.2 andrew 200: }
201: }
202:
203: sub get_ver
204: {
205: my $host = shift;
206: return cmd($host, 'ver');
207: }
208:
209: sub enable_tftpd
210: {
211: my $host = shift;
212:
1.3 andrew 213: my $vals = cmd($host, 'tftpd on', 'Success.');
1.2 andrew 214:
215: if ($vals->{'Tftpd'} eq 'listen') {
1.3 andrew 216: return $vals;
1.2 andrew 217: } else {
218: return undef;
219: }
220: }
221:
222: sub check_tftpd
223: {
224: my $host = shift;
1.3 andrew 225: return cmd($host, 'tftpd', 'Success.');
226: }
227:
228: sub sysinfo
229: {
230: my $host = shift;
231: return cmd($host, 'sysinfo', 'Success.');
232: }
233:
234: sub updateflash
235: {
236: my $host = shift;
237: my $old = shift;
238: my $new = shift;
239:
240: return undef unless $new;
241:
242: return cmd($host, "updateflash mainimage $old $new", 'Success.', 90);
1.2 andrew 243: }
244:
245: sub cmd
246: {
247: my $host = shift;
248: my $string = shift;
1.3 andrew 249: my $expect_last = shift;
250: my $timeout = shift || 5;
1.2 andrew 251:
1.3 andrew 252: my @lines = $host->cmd(String => $string, Timeout => $timeout);
1.2 andrew 253:
254: my $vals = decode_lines(@lines);
1.3 andrew 255:
256: my $last = $host->lastline;
257:
258: unless ($expect_last) {
259: return $vals;
260: }
261:
262: if ($last =~ /$expect_last$/) {
263: return $vals;
264: } else {
265: warn "Error with command ($string): $last";
266: return undef;
267: }
1.2 andrew 268: }
269:
270: sub decode_lines
271: {
272: my @lines = @_;
273:
274: my %conf;
275:
276: my $key = '';
277: my $val = '';
278: my $in_key = 0;
279: my $in_val = 0;
280:
281: foreach my $line (@lines) {
282: my @chars = split //, $line;
283:
1.3 andrew 284: my $last_key = '';
1.2 andrew 285: foreach my $c (@chars) {
286:
1.3 andrew 287: if ($c eq '[' || $c eq "\r" || $c eq "\n") {
288: if ($c eq '[') {
289: $in_key = 1;
290: $in_val = 0;
291: } else {
292: $in_key = 0;
293: $in_val = 0;
294: }
295:
1.2 andrew 296: if ($key) {
1.3 andrew 297: $key =~ s/^\s+//;
298: $key =~ s/\s+$//;
299:
300: $val =~ s/^\s+//;
1.2 andrew 301: $val =~ s/\s+$//;
1.3 andrew 302:
303: if ($key eq 'Checksum' && $last_key) {
304: # Special case for these bastids.
305: my $new = $last_key;
306: $new =~ s/\s+\S+$//;
307: $key = $new . " " . $key;
308: }
309:
310: $last_key = $key;
1.2 andrew 311: $conf{$key} = $val;
312: $key = '';
313: $val = '';
314: }
315:
316: } elsif ($c eq ']') {
317: $in_val = 1;
318: $in_key = 0;
319: $c = shift @chars;
320:
321: } elsif ($in_key) {
322: $key .= $c;
323:
324: } elsif ($in_val) {
325: $val .= $c;
326: }
327: }
328: }
1.3 andrew 329: #print Dump \%conf;
1.2 andrew 330:
331: if (%conf) {
332: return \%conf;
333: } else {
334: return \@lines;
335: }
336: }
1.1 andrew 337:
1.3 andrew 338: sub read_conf
339: {
340: my $file = shift;
341: my %conf;
342: my $in_ip_list = 0;
343: open my $fh, $file or die "Couldn't open file $file: $!";
344: while (<$fh>) {
345: chomp;
346: next if /^#/;
347: next if /^$/;
348: if ($in_ip_list) {
1.7 andrew 349: if (/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.)(\d{1,3})-(\d{1,3})/) {
350: push @{ $conf{'ips'} }, $1 . $_ for ($2..$3);
351: } else {
352: push @{ $conf{'ips'} }, $_;
353: }
1.3 andrew 354: } else {
355: my ($key, $val) = split /\s+/, $_, 2;
356:
357: if (lc($key) eq 'ips') {
358: $in_ip_list = 1;
359: next;
360: }
361:
362: $conf{ lc($key) } = $val;
363: }
364: }
365: close $fh;
366:
367: #print Dump \%conf;
368: foreach (
369: 'password',
370: 'file_name', 'file_size', 'file_cksum',
371: 'ver', 'cksum', 'ips',
372: ) {
373: die "No $_ specified in config file!"
374: if (not exists $conf{$_});
375: }
376:
1.7 andrew 377: #print Dump \%conf;
378: #exit;
1.3 andrew 379: return \%conf;
1.4 andrew 380: }
381:
382: sub mylog
383: {
1.5 andrew 384: my $thing = shift;
385: chomp $thing;
386: my $save = shift;
387:
388: print $thing, "\n";
389:
390: if (defined $save && $save == LOG_SAVE) {
391: unless ($MYLOG) {
392: open ($MYLOG, '>>', $log_file)
393: or die "Couldn't open logfile!\n";
394: my $ofh = select $MYLOG;
395: $|=1;
396: select $ofh;
397: mylog("Opened log ($log_file)");
398: }
1.4 andrew 399:
1.5 andrew 400: flock($MYLOG, LOCK_EX);
401: print $MYLOG (scalar gmtime), "\t", $thing, "\n"
402: or die "Couldn't print to MYLOG: $!";
403: flock($MYLOG, LOCK_UN);
404: }
1.4 andrew 405: }
406:
407: sub GetLogName
408: {
409: my $prefix = shift || die "Invalid prefix passed for log";
410:
411: my $logdate = GetLogDate();
412: my $logver = 0;
413: my $logname;
414:
415: do {
416: $logname = $prefix . $logdate . sprintf("%02d", $logver) . '.log';
417: $logver++;
418: } until (not -e $logname);
419:
420: return $logname;
421: }
422:
423: sub GetLogDate
424: {
425: my ($sec,$min,$hour,$mday,$mon,$year,,,) = localtime();
426:
427: $mon++;
428: $year += 1900;
429:
430: if ($min < 10) { $min = "0$min" }
431: if ($sec < 10) { $sec = "0$sec" }
432: if ($hour < 10) { $hour = "0$hour" }
433: if ($mday < 10) { $mday = "0$mday" }
434: if ($mon < 10) { $mon = "0$mon" }
435:
436: my $time = $year . $mon . $mday;
437:
438: return $time;
1.3 andrew 439: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>