#!/usr/bin/perl ############################################################################ # Copyright 2006 University of Washington. All Rights Reserved. # Licensing and technical questions: tv-streaming@cac.washington.edu # Created by Matt Hodge, SMT, Computing & Communications. ############################################################################ # TODO: d type use strict; $| = 1; use Socket; use Fcntl; use Errno; my @descs = ( [ '0', 'uw_tx', '198.48.79.16:4000' ], [ '1', 'tampa_tx', '198.48.76.132:4000' ], [ '2', 'madison', '128.104.23.68:4000' ], [ '3', 'uw_tx2', '198.48.79.24:4000' ], ); # # connect to all txs # my @entrys; for my $desc (@descs) { my $entry = { 'id' => $desc->[0], 'name' => $desc->[1], 'addr' => $desc->[2], }; print "connecting to $entry->{'id'} $entry->{'name'}, $entry->{'addr'}\n"; $entry->{'sock'} = socket_connect($entry->{'addr'}); defined($entry->{'sock'}) || die "connect to $entry->{'id'} failed"; mpe_parse_init($entry->{'mpe_state'} = {}, 1); $entry->{'mpe_status'} = 'CONTINUE'; push(@entrys, $entry); } for(;;) { print "\n"; for my $entry (@entrys) { print "$entry->{'id'} = $entry->{'name'}, $entry->{'addr'}\n"; } print "off = all senders stop sending\n"; print "command: "; my $line = ; defined($line) || last; $line =~ s/\s+$//s; my @actions; if($line =~ m/^off\s+(\d+)$/s) { my $id = $1; for my $entry (@entrys) { if($entry->{'id'} eq $id) { push(@actions, [ $entry, 'none' ]); } } } elsif($line eq 'off') { for my $entry (@entrys) { push(@actions, [ $entry, 'none' ]); } } elsif($line =~ m/^\d+$/s) { my $id = $&; my $action_last; for my $entry (@entrys) { if($entry->{'id'} eq $id) { $action_last = [ $entry, 'subrem' ]; } else { push(@actions, [ $entry, 'sub' ]); } } defined($action_last) && push(@actions, $action_last); } for my $action (@actions) { my ($entry, $cfg) = @$action; my $cmd = "(wkr i cfg_select $cfg)"; print "$entry->{'id'} $entry->{'name'}: $cmd\n"; my @rest; ($entry->{'status'}, @rest) = send_recv($entry->{'sock'}, "$cmd\n", $entry->{'status'}, $entry->{'mpe_state'}); if($entry->{'status'} eq 'OK') { print " RESPONSE: ".mpe_string($rest[0])."\n"; } else { die join(' ', $entry->{'status'}, @rest); } } } for my $entry (@entrys) { close($entry->{'sock'}); } exit(0); ################################################################### # composite functions ################################################################### sub send_recv { my ($sock, $send_buf, $status, $mpe_state) = @_; my $z = length($send_buf); if(! defined(my $x = send($sock, $send_buf, 0))) { return ('SEND_ERROR', $!); } elsif($x != $z) { return ('SEND_ERROR', "$x/$z"); } return(recv_mpe($sock, $status, $mpe_state)); } sub recv_mpe { my ($sock, $status, $mpe_state) = @_; my @rest; if($status ne 'CONTINUE') { goto loop_post_recv; } loop: my $buf; if(! defined(recv($sock, $buf, 8192, 0))) { return ('RECV_ERROR', $!); } elsif(! length($buf)) { mpe_parse_add($mpe_state, '', 1); } else { mpe_parse_add($mpe_state, $buf); } loop_post_recv: ($status, @rest) = mpe_parse($mpe_state); if($status eq 'CONTINUE') { goto loop; } return ($status, @rest); } ################################################################### # mpe_parse ################################################################### sub mpe_parse_init { my ($state, $single) = @_; $state->{'single'} = $single; $state->{'src'} = ''; $state->{'src_end'} = undef; $state->{'src_pos'} = 0; $state->{'tok_state'} = 'WS'; # $state->{'tok_start'} # $state->{'tok'} $state->{'stack'} = []; $state->{'syms'} = []; } sub mpe_parse_add { my ($state, $part, $end) = @_; $state->{'src'} .= $part; $state->{'src_end'} = $end; } sub mpe_parse { my ($state) = @_; for(;;) { for( ; ; $state->{'src_pos'}++) { if($state->{'src_pos'} >= length($state->{'src'})) { if(! $state->{'src_end'}) { return ('CONTINUE'); } elsif($state->{'tok_state'} eq 'NORMAL') { goto sym_str; } elsif($state->{'tok_state'} eq 'WS') { if(@{$state->{'stack'}}) { return ('UNMATCHED_LP'); } elsif(@{$state->{'syms'}}) { goto ok; } else { return ('DONE'); } } else { return ('TOK_INCOMPLETE'); } } my $c = substr($state->{'src'}, $state->{'src_pos'}, 1); if($state->{'tok_state'} eq 'WS') { if($c !~ m/\s/) { $state->{'tok_state'} = 'NORMAL'; $state->{'tok_start'} = $state->{'src_pos'}; $state->{'tok'} = ''; goto tok_normal; } } elsif($state->{'tok_state'} eq 'NORMAL') { tok_normal: if($c eq '"') { $state->{'tok_state'} = 'DQ'; } elsif($c eq "'") { $state->{'tok_state'} = 'SQ'; } elsif($c eq "\\") { $state->{'tok_state'} = 'BS'; } elsif($c eq '(') { if($state->{'src_pos'} > $state->{'tok_start'}) { goto sym_str; } $state->{'src_pos'}++; goto sym_lp; } elsif($c eq ')') { if($state->{'src_pos'} > $state->{'tok_start'}) { goto sym_str; } $state->{'src_pos'}++; goto sym_rp; } elsif($c =~ m/\s/) { goto sym_str; } else { $state->{'tok'} .= $c; } } elsif($state->{'tok_state'} eq 'BS') { $state->{'tok'} .= $c; $state->{'tok_state'} = 'NORMAL'; } elsif($state->{'tok_state'} eq 'BS_DQ') { $state->{'tok'} .= $c; $state->{'tok_state'} = 'DQ'; } elsif($state->{'tok_state'} eq 'DQ') { if($c eq '"') { $state->{'tok_state'} = 'NORMAL'; } elsif($c eq "\\") { $state->{'tok_state'} = 'BS_DQ'; } else { $state->{'tok'} .= $c; } } elsif($state->{'tok_state'} eq 'SQ') { if($c eq "'") { $state->{'tok_state'} = 'NORMAL'; } else { $state->{'tok'} .= $c; } } } sym_str: if(@{$state->{'stack'}}) { push(@{$state->{'stack'}->[$#{$state->{'stack'}}]}, $state->{'tok'}); } else { push(@{$state->{'syms'}}, $state->{'tok'}); } goto sym_done; sym_lp: push(@{$state->{'stack'}}, []); goto sym_done; sym_rp: if(defined(my $sym = pop(@{$state->{'stack'}}))) { if(@{$state->{'stack'}}) { push(@{$state->{'stack'}->[$#{$state->{'stack'}}]}, $sym); } else { push(@{$state->{'syms'}}, $sym); } } else { return ('UNMATCHED_RP'); } goto sym_done; sym_done: $state->{'tok_state'} = 'WS'; if((! @{$state->{'stack'}}) && $state->{'single'}) { goto ok; } } ok: my $syms = $state->{'syms'}; $state->{'syms'} = []; return ('OK', @$syms); } ############################################################################ # mpe_string ############################################################################ sub mpe_string { my ($mpe) = @_; if(ref($mpe)) { return '( '.join(' ', map { mpe_string($_) } @$mpe).' )'; } elsif(! length($mpe)) { return '""'; } elsif($mpe =~ m|[^A-Za-z0-9_./-]|s) { $mpe =~ s/[\"\\]/\\$&/sg; return '"'.$mpe.'"'; } else { return $mpe; } } ############################################################################ # socket functions ############################################################################ sub packaddr { my ($host, $port) = split(/:/, $_[0]); my (undef, undef, undef, undef, $hostaddr) = gethostbyname($host); return pack('S n a4 x8', &AF_INET, $port, $hostaddr); } sub socket_connect { my ($addr) = @_; local *SOCK; if(socket(SOCK, &PF_INET, &SOCK_STREAM, 0)) { if(connect(SOCK, packaddr($addr))) { return *SOCK; } else { print STDERR "$!, connect failed\n"; } close(SOCK); } else { print STDERR "$!, socket failed\n"; } return; }