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