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