A Walk through the Code
This version of the code can be considered as a
prototype still under QA test. However, it has been doing fine on my local
vhf and hf networks with only a few "glitches" from certain types of
atypical broadcasts. The refinement and testing phases continue. At the
very least, this release serves as a declaration of the specifications
from which code developers could work. This is a very basic module and could
eventually be encorporated into a more general server. For now, in this
phase though, it remains as standalone code for better error detection.
NOTE: I have made a few very slight modifications to the code. This
involves the actual filter for beacons which previously attempted to pick
up any line with the word beacon in it. It has been changed to ">beacon"
to pick up just this class of broadcast...
- The Perl Source Code
-
|
abscopy.plx: the Receiver Module
|
#! /usr/bin/perl -w
# abscopy.plx
# 08-12-04, 08-19-04
# 10-15-04
# KA1FSB/kbn
#
# ======================================================================
# * AUTOMATIC BEACON SYSTEM *
#
# File: abscopy.plx
#
# Version: 1.0.1
#
# Author: K. Norton
#
# Description: Connects to trace server 1236 and monitors, no input
# from user, just the initial instruction line to
# the JNOS Trace Server...
#
# Output to a device-related logger file, so may start more
# than one per device. (abscopy.plx ax0 &)
#
# Stay "up" forever... take down with a kill -9
#
# =======================================================================
#----- Globally applies to all modules...
require 5.002;
use strict;
use Socket;
#----- Signal/interrupt area...
$SIG{ALRM} = \&timed_out;
#===== Subroutine area...
sub timed_out
{
#----- This shuts off the timer with message in $@...
die "TO";
}
sub open_sock
{
#----- Build the socket connection...
#----- Have system assign you a port number, if not specified.
#----- We have requested/specified a port number... 1236
my $iface = shift; # Such as hf or ax0
my $discode = shift; # The display code for trace
my $remote = shift; # The target ip
my $port = shift; # port is 1236
my $iaddr = "";
my $paddr = "";
my $proto = "";
my $cmd = "";
$port = getservbyname($port, 'tcp') if ($port =~ /\D/);
#----- You need a port to build a socket connection!
die "No port... stopping here..." unless $port;
#----- You need a host machine... then create an internet address
$iaddr = inet_aton($remote) or die "No such host: $remote";
$paddr = sockaddr_in($port, $iaddr);
#----- Using tcpip here
$proto = getprotobyname('tcp');
#----- Create socket the tcpip "SOCK"
socket(SOCK, PF_INET, SOCK_STREAM, $proto) or die "Socket: $!";
#----- Make the "link" and connect to host for data stream...
connect(SOCK, $paddr) or die "Connect: $!";
#----- Prepare server "instructions" for this session with these params
#----- for this particular port...
$cmd = "trace " . $iface . " " . $discode;
#----- Prepare for immediate output on this "handle"
select(SOCK);
$| = 1;
#----- NOTE: The telnet protocol needs "\r\n" at the end!!!
#----- Instruct the server... send it!
print SOCK "$cmd\r\n";
return *SOCK;
}
sub monitor
{
#----- Open at least one output file, could be more to write to...
#----- If not a named pipe file, use tail -f dev-.log to see data
#----- Then stay here and loop...
my $mypath = shift;
my $iface = shift;
my $delay = shift;
my $socket = shift;
my $traceout = "";
my $lastline = "";
my $line = "";
my $record = "";
my $key = "";
my $callsign = "";
my $type = "";
my $rcline = "";
my $rest = "";
my $tdstamp = "";
my $lastrec = "";
my $textrec = "";
#----- Change if you wish...
my $devprefix = "dev-";
#----- First open the output file...
$traceout = $mypath . "/" . $devprefix . $iface . ".log";
open(FILEOUT, ">>$traceout") or die "Cannot open output file";
#----- Must do this if spawned from a server, else no output
select(FILEOUT);
$| = 1;
#----- This is the "forever" monitoring loop...
$lastline = "";
$line = "";
$lastrec = " ";
UPPER: while ( 1 )
{
$record = "";
$rcline = "";
$line = "";
while(defined($line = <$socket>)) {last;}
next UPPER unless $line;
#----- Condition the line, chop the "\r\n" EOL...
$rcline = $line;
$rcline =~ s!\r\n$!!;
$callsign = "";
$type = "";
#----- Filter this line and assign the secondary part of the key
#----- Modify as required... if anything make it shorter
if ( $rcline =~ />ID|>BEACON|>CQ|>MAIL|>TEST|>QST/o )
{
$rest = "";
if ( ($callsign, $rest) = split(/->/, $rcline, 2) )
{
if ( $rest =~ /MAIL/o )
{
$type = "MAIL";
}
elsif ( $rest =~ /BEACON/o )
{
$type = "BEACON";
}
elsif ( $rest =~ /ID/o )
{
$type = "ID";
}
elsif ( $rest =~ /CQ/o )
{
$type = "CQ";
}
elsif ( $rest =~ /TEST/o )
{
$type = "TEST";
}
elsif ( $rest =~ /QST/o )
{
$type = "QST";
}
else
{
#----- Just in case something goes "haywire"
$type = "";
}
}
}
else
{
#----- If didn't find identifiers on the master list, then go back...
next UPPER;
}
#----- Must find a type on the list...
next UPPER unless $type;
#----- Could have a callsign test as per above if needed...
# next UPPER unless ... ;
#----- This is an optional feature! It attempts to block rapidly sequential
#----- CQ's, TEST's, or QST's, while always passing ID's, BEACON's,
#----- or Mail's.
#----- Either use this or "if" code block below, not both...
# next UPPER if $lastline =~ /$line/;
#----- This is the pass-thru list... always let through...
if ( $type !~ /ID|BEACON|MAIL/o )
{
#----- Return immediately repetitive CQ's, TEST's, QSTs...
next UPPER if $lastline =~ /$line/;
}
#----- Build the composite key from "header" line, then build record...
#----- A time stamp date can go here, could add julian number too, %j:
$tdstamp = "";
$key = "";
chomp($tdstamp = `/bin/date +%H:%M:%S`);
$key = $callsign . ":" . $type;
$record = $key . " " . $tdstamp . " ";
#----- Store last line for comparison
$lastline = $line if $line;
#----- Now read the record details if any exist...
#----- Exit socket wait after the "delay" period
RECLOOP:
{
$rcline = "";
eval
{
alarm($delay);
while(defined($line = <$socket>)) {last;}
alarm(0);
};
if ( $@ =~ /TO/o )
{
<$socket>;
last RECLOOP;
}
$rcline = $line;
$rcline =~ s!\r\n$!!;
#----- Optional char filter for record... don't print unprintables...
$rcline =~ tr[\0-\011\013-\037\177][]d;
#----- When start of next data block arrives, stop record acculumlation
#----- NOTE: This is specific to the JNOS Trace Server application!!!
last RECLOOP if ( $rcline =~ /^$iface /o );
#----- NOTE: Not recording this data! But could optionally append...
if ( $type =~ /MAIL/ && $rcline =~ /Active /o )
{
$record = "";
last RECLOOP;
}
if ( $rcline )
{
#----- New line subsitute is the pipe "|" char, could use something different
$record .= $rcline . "|";
#----- Just store last line of text body...
$textrec = $rcline . "|";
}
redo RECLOOP;
}
#----- Now write record to screen/file...
#----- Could split to different logger files too by type: ID, BEACON, etc
#----- Don't print repetitive record texts ... comment out if not needed...
if ( $lastrec ne $textrec )
{
print FILEOUT "$record\n";
$lastrec = $textrec;
}
#----- Bottom of "forever" while loop
}
}
# ------------------------------------------------------------------
# Main
# ------------------------------------------------------------------
#----- These are default params for the Traceserver on JNOS machine...
#----- May be passed in...
my $iface = shift || "hf"; # The interface to trace
my $discode = shift || 311; # The display code/mode
my $remote = shift || "44.56.26.11"; # Enter as arg or this
my $port = shift || 1236; # The trace port on JNOS
#----- Initializations...
my $mypath = "/root/uptoisp/db105"; # "Home" directory
my $delay = 15; # Get "last rec" after delay (15-30)
#----- Now build the socket connection and open it for read/write...
my $socket = &open_sock($iface, $discode, $remote, $port);
#----- Create an output log file then enter a continuous loop
#----- and monitor packet activity until a quit/kill...
&monitor($mypath, $iface, $delay, $socket);
#----- Never gets here...
exit;
#----- End of abscopy.plx
|
Probably one of the more interesting uses of eval is its ability, in this
case, to spawn a child timer process using the alarm function. This "solves"
the last record problem. Without a "break-out" signal, the last record might
remain hidden until a real packet comes along, EOR end-of-record, to
trigger the close on the record. The alarm will force a close, thus
ensuring that all data is written to the logger file without
protracted delays.
Also, all instances of a new line, or new line carriage return, are swapped
with the pipe "|" character. If someone gets "fancy" and starts using that
character in broadcasts, then another more "obtuse" character may have to
be chosen. (You might want to use something different anyway...)
The script is set to ignore immediately multiple broadcasts for CQ, TEST,
and QST. Some stations will send out several CQ's back-to-back and there is
a "trap" near the top of the filter loop to ignore these multiples.
However, all BEACONS and IDs are passed though, unless the content of the
broadcast is the same. This avoids the ACKs for relayed beacon/id
broadcasts. The code checks the last line of any message and prints it
to a file if it is different from the preceeding last line, more extensive
testing could be done. All of the above "filters" may be commented out
if found to be too restictive or inappropriate to your net operations.
They were included here to "document" the options if needed.
- How to Install and Run
-
You may locate this code anywhere you like. Just be
sure to edit the mypath variable above so that it reflects the real
path you are using. Be sure to chmod the code with chmod +x
abscopy.plx so that it does become executable. And replace my JNOS IP
with your IP into the application. To start it up, use:
- abscopy.plx ax0 &
... or ...
- ./abscopy.plx ax0 &
where you replace ax0 with your actual device if it differs... The code
creates a logger file named dev-ax0.log and begins to write data to it as
heard...
You may continue to start up as many copies of the code as you have devices
to monitor. I start one for an hf device too:
- abscopy.plx hf &
... or ...
- ./abscopy.plx hf &
Again, the code creates a log file named dev-hf.log and begins to write data
to that file.
And remember, this code connects to a socket on JNOS, 1236, which links
to the Trace Server, so that server must be enabled for your
JNOS system, or it won't be able to complete that link and this code won't
work! So just copying this code and activating it is not enough. Be sure
that your Trace Server is up and running and can take "calls" from
clients, which is what this code is, a filtering client!
- Some Typical Output and what to do with it
-
What does the output look like? It goes to a file,
or possibly a pipe, and therefore you won't see any immediate output from
the code on the screen as in a typical trace operation . The easiest way
to prove that you have output is to "cat" the logger file. Here is a
sampled excerpt from the dev-ax0.log file:
|
Sample Output: dev-ax0.log
|
N1IQI:BEACON 05:47:27 Loren,Bryantville,Ma South Shore Packet *ORS*NTS*|
N1OTX-4:BEACON 05:49:59 19-Aug 05:00 < South Shore Packet Group BBS - Woburn MA >|
950 Active Messages (FBB Ver 7.04j)|
Messages For: |
None|
W1JOE-7:BEACON 05:52:45 Experimental node Brockton Ma sysop W1GMF|
KD1ZX:BEACON 05:54:04 kd1zx in Central Falls RI cfri/d|
does anyone do tcp/ip over packet? email hvinelinux@hotmail.com please :-)|
W1JOE-7:ID 05:54:33 W1JOE-7/R BROCK/D W1JOE-3/B|
W1GMF-4:BEACON 06:00:04 19-Aug 06:00 < South Shore Packet Group BBS >
765 Active Messages |
(FBB Ver 7.04j)|
Messages for: |
W1GMF|
**** CHECK OUT THE DX CLUSTER ON 145.09 -- C WD1L V W1MV ****|
|
W1MV:ID 06:00:11 W1MV:BRIDGE - (X)NET / FLEXNET NODE & DIGI (BRIDGEWATER,MASS)|
N1OTX-4:BEACON 06:04:59 19-Aug 05:15 < South Shore Packet Group BBS - Woburn MA >|
951 Active Messages (FBB Ver 7.04j)|
Messages For: |
None|
W1JOE-7:ID 06:08:15 W1JOE-7/R BROCK/D W1JOE-3/B|
|
I have added some new line characters (\n) so that the logger file
is easier to read here.
Here is another sample from the dev-hf.log file:
|
Sample Output: dev-hf.log
|
KA1FSB-7:ID 06:40:11 JNOS/Linux 1.11f * Network 105 * 14.105<>145.090 [Wellesley, MA]|
WD4KAV-1:ID 07:19:25 WD4KAV JNOS Pt. St. Lucie FL (WD4KAV-7 Node, WD4KAV-1 PBBS)|
WD4KAV-1:MAIL 07:28:54 Mail for: KB9PVH N2TIF|
K9VSO:ID 08:13:20 K9VSO/R K9VSO-2/G K9VSO-1/B K9VSO-7/N|
W4ZEF:CQ 08:15:25 Art Vero Beach FL HF 14.105 VHF 145.530 Gate W4ZEF-7,PMB/1,Digi |
WD4KAV-1:MAIL 08:28:57 Mail for: KB9PVH N2TIF|
K9VSO:BEACON 08:36:33 K9VSO Monroe Center,Wi EN54bc|
NETWORK105 Emergency & Chat|
~~~ 145.030 < > 14.105 ~~~|
~~~ SYSOP: K9VSO ~~~|
W4ZEF:CQ 09:05:23 Art Vero Beach FL HF 14.105 VHF 145.530 Gate W4ZEF-7,PMB/1,Digi |
KC7GNM:BEACON 09:14:19 KC7GNM-7 node, -2 gateway, -1 pbbs. HF/VHF Gateway in Robins AFB, Ga.|
WD4KAV-1:ID 09:19:37 WD4KAV JNOS Pt. St. Lucie FL (WD4KAV-7 Node, WD4KAV-1 PBBS)|
K9VSO:ID 09:24:31 K9VSO/R K9VSO-2/G K9VSO-1/B K9VSO-7/N|
|
And, again, for the long lines, I have added new lines here for readability.
These files are really "raw" data files and need some further interpretation
to be displayed in a readable format. Here is a very simple shell command
line to view these records:
- cat dev-ax0.log | tr '|' '\n' | more
This will display the records as they arrived in a presentable
format. Sometimes you need to see the "raw" data but in a more readable
format. The above command line will show you the contents at a glance.
Here is a shell command line, which uses the follow switch for tail, that
will display the contents as they arrive live at your station...
- tail -f dev-ax0.log | tr '|' '\n' &
(This will use double spacing between records. If you don't want that,
then just use the tail part of the command.)
Having the data for a device stored persistently, and easily accessible,
can be a great asset for acting on BEACON/ID alerts. There are numerous
sorts and greps that can be applied to the "raw" file in Linux. For example,
you could call out a sequence for any given station and sort by time. This
would show any changes in messages carried by the BEACONS/IDs. You now have
a complete record along a time line for all the day's beaconing events.
The shell script code following may give you some ideas as to how to
implement these methods...
- A Slightly More Elaborate Displayer
-
If you need to see the output in an easily
readable format, and in one that simulates a typical trace function, then
the following somewhat "primative" shell script program will sort the
named logger file by date, last heard, and then do a sort unique by
callsign. Here is that script:
|
Displayer Script: display
|
#! /bin/sh
# display
# 07-18-04
# 08-20-04
# KA1FSB/kbn
# ----------------------------------------------------------------
# Format and display this file. This is a basic display script.
# The input file is the local mheard.txt file!
#
# Now inlcudes a "call" to do_mysort to get a unique list...
# Usage: display ax0 | more, or display hf | more
# ----------------------------------------------------------------
#===== Subs...
#----- Routine to sort a logger file...
do_mysort ()
{
#----- Inits & defaults...
myfile=""
bigBuf=""
devtype="$1"
if [ "$devtype" ]
then
myfile="dev-${devtype}.log"
fi
#----- Sort & write data to output file...
#----- You may try different sorters here for fun!
sort -k1,1 -k2,2r $myPath/${myfile} | \
sort -u -k1,1 | pr -t -n > $myPath/mheard.txt
}
#===== Main
#----- Inits...
line=""
title=""
body=""
device=""
off="\033[m"
bld="\033[1m"
myPath="/root/other/db105"
#----- Check for args...
if [ "$1" ]
then
device=$1
else
#----- Your default device...
device="hf"
fi
#----- Call this sort routine...
do_mysort $device
#----- Start at screen top
clear
#----- Spin thru the data file...
while [ 1 ]
do
read line || break
title=`echo "$line" | cut -f1-5 -d " "`
body=`echo "$line" | cut -f6-500 -d " "`
echo -e "${bld}${title}${off}"
echo "$body" | tr '|' '\n' | pr -t -o6
done < $myPath/mheard.txt
#----- End of display
|
You may have to "adjust" lines of this code to get it to work properly on
your system as in all differing UNIX/Linux environments. For example, on
my Slackware development platform, the cut commands are specified as above.
However, on my SuSE platform, the first cut ends at 2 (-f1-2) and the next
line begins at 3 (-f3-500). You will have to experiment to see how it runs
on your machine. Also, the "pr -t -o6" may need to be changed to "pr -t -o10".
You can format this display anyway you choose! Whatever conforms to your
network, or personal format, style!
- Caveats
-
As always, you use this code at your own risk.
I have done my best to carry out extensive QA'ing, but there is no
code that does not have some unforseen consequences, translate "bugs,"
lurking in the wings... even though I "compiled" it with the "NO-BUGS"
switch :)
Also, this was developed on Linux OS 2.0.30 and in a Slackware package. If
you run from another package, like SuSE for example, even though the
application environment has been standardized a la Perl, there still may be
subtle differences to watch out for in socket IO and file handling that
are dependent on the OS. Just a "heads up" for general usage. "Beat the
heck" out of it and keep any eye on it ... and modify as required.
- Conclusions
-
I hope this short article has inspired you to
try this NOS-ABS approach to opening up the BEACON/ID potential for *NOS!
Use this code as a springboard for future developement on your system.
Integrate it into your everyday operations with a view toward making your
NOS station a true broadcasting node with emergency capability! Let me know
if you have moved the "boulder" one micron ahead... Have fun and stay
on-the-alert with NOS-ABS!
(Courtesy KBNorton Computer Services)
|