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