1.71.5.204-B20160823
Today's ARexx is something for someone who runs a network of machines (with an Amiga, of course) and wants to check if all the machines are running okay during the day. This might be especially useful if you're not in the same location as the machines.
What this does is ping a list of network names, one at a time, and checks if there is a response. If there is, it logs the ping time, or if not just logs the machine as "Down". It continues doing this until broken with Ctrl-C. When broke, or every hour, it also prints a summary to the log which is in the format of the standard Ping summary.
Sadly, being ARexx, the ping times are badly out for local machines (ARexx latency seems to be around 20ms - fine for the Internet, when a machine might be 500ms away, but on a LAN you'd often expect <1ms). This isn't a problem for us, as this script only wants to see if the machine is up and running.
Here's an example log file, set to loop every second or so - normally, you'd only want this to loop every five minutes or half hour unless you're controlling "mission critical" machines (see configurable bits of the script).
--------------------------------------------------------------------
20010902.15:54:48 | nuke mono burn rhic bauen
20010902.15:54:49 | 26.2 28.77 26.36 26.54 Down 20010902.15:54:51 | 21.15 23.66 22.17 22.33 Down 20010902.15:54:54 | 19.49 21.94 21.05 21.16 Down 20010902.15:54:56 | 18.78 21.02 20.4 20.53 Down 20010902.15:54:58 | 18.26 20.86 19.6 19.71 Down
Hourly network response statistics 02/09/01 15:55:00 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
nuke - 5 packets transmitted, 5 packets received, 0% packet loss. round-trip min/avg/max = 16.1/18.26/26.21 ms
mono - 5 packets transmitted, 5 packets received, 0% packet loss. round-trip min/avg/max = 18.26/20.86/28.78 ms
burn - 5 packets transmitted, 5 packets received, 0% packet loss. round-trip min/avg/max = 16.41/19.6/26.37 ms
rhic - 5 packets transmitted, 5 packets received, 0% packet loss. round-trip min/avg/max = 16.43/19.71/26.55 ms
bauen - 5 packets transmitted, 0 packets received, 100% packet loss.
--------------------------------------------------------------------
The top part is the constantly (per configured interval) updated bit, the bottom part is output every hour or when you Ctrl-C it.
In this example, there are four machines running and one down (in this case, it's switched off and not even phyically connected to the network).
'nuke' is the localhost, so you can see what the ping latency is like.
Something I plan to do later is a script to graph this data and pull out more useful information (e.g "host burn was down between 1am and 9am").
The script doesn't hold the log file open, so you can easily set up something to do this on a weekly cron job:
rename logfile to logfile.date
archive logfile.date with lha/lzx
net/email logfile.date to your personal mailbox
delete logfile.date
External libraries: this uses a few, available on AmiNet or your local BBS:
* RXsocket
* RexxTricks
* RMH (RexxMustHave) -- this appears to require rxwiz.library installed too.
The program requires an external file which holds the names or IP addresses of each machine you want to monitor. Very simple, one machine to a line in a file named LANstatus.config:
--[ LANstatus.config ]------------------------------
nuke
mono
burn
rhic
bauen
----------------------------------------------------
Here's the script itself. As before, you'll need to go over every line to configure it for yourself (but this is AMIGA_PROG, and this is a programming exercise, so go do it!) - although there is a configurable bit. Particularly, "waitloop" should be set to a value you're comfortable with. I recommend between 5 and 30 minutes, but it's set to 1 minute here so you can get immediate results.
--[ LANstatus.rexx ]--------------------------------
/* LANstatus.rexx
*
* Pings a group of machines, and reports in
* a log if those machines were up.
* Neil Williams
neil@zeusdev.co.uk
* 2/9/2001.
*
* Parts based on ping.rexx from rxsocket
* examples.
*/
parse arg args
options cache
options failat 99
options results
signal on break_c
if ~show("L","rexxsupport.library") then
if ~addlib("rexxsupport.library",0,-30) then do
say "no rexxsupport.library"
exit
end
if ~show("L","rxsocket.library") then
if ~addlib("rxsocket.library",0,-30) then do
say "no rxsocket.library"
exit
end
if ~show("L","rmh.library") then
if ~addlib("rmh.library",0,-30) then do
say "no rmh.library"
exit
end
if( ~show( 'l', "rexxtricks.library" ) ) then
if( ~addlib( "rexxtricks.library", 0, -30, 0 ) ) then do
say "Could not open rexxtricks.library"
exit
end
/* nb. for an AmiNet release, implement args - RMH has an excellent
readargs() function:
if ~RMH_ReadArgs("HOST/A,SIZE/K/N,NUM/K/N,QUICK/S") then do
call PrintFault(IoErr(),prg)
exit
end*/
/*
*/
dataSize = 56 /* Size of ping packet */
wait = 1 /* Seconds to wait for response from pings. Set to one
* unless one of your machines is on the other side of
* the planet! */
waitloop = 1*60 /* Seconds to wait between each complete check of all
* machines (x*60 for values of x minutes)
*/
logfile = 'LOGS:LANstatus.log'
/* Read a list of machines to check into stem LAN
*/
if ~rxtr_readfile( 'LANstatus.config', 'LAN' ) then do
say 'Error on config file LANstatus.config'
exit
end
/** create a RAW ICMP socket **/
sock=socket("INET","RAW","ICMP")
if sock<0 then call err("no socket")
/** socket must NOT blocking **/
call IOCtlSocket(sock,"FIONBIO",1)
/** we need an ID to recognize our icmp **/
ourID=right(x2c(pragma("ID")),2)
/** create a timer **/
t=CreateTimer()
timSig=TimerSignal(t)
/* Initalise and write log header
*/
line = date('S') ||'.'|| time() || ' |'
do i = 1 to LAN.0
LAN.i.min=1E10
LAN.i.max=0
LAN.i.avg=0
LAN.i.nrec=0
LAN.i.ntrans=0
LAN.i.UP=0
line = line || ' ' || left( LAN.i, 10 )
end
/* Wow, what lazy coding.. */
if ~exists( logfile ) then
address command 'echo >LOGS:LANstatus.log'
if open('MyCon',logfile, 'a') then do
writeln( 'MyCon', line )
writeln( 'MyCon', '' )
call close( 'MyCon' )
end
/* Main Loop.
* Networking code is mostly from rxsocket ping from here
*/
running = 1
brunning = 1
do while brunning
do i = 1 to LAN.0
LAN.i.UP = 0
/** resolve host name **/
sin.addrAddr=resolve( LAN.i )
if sin.addrAddr ~== "-1" then do
call StartTimer(t,wait)
call sendPacket(dataSize)
sel.read.0=sock
res=WaitSelect("SEL",,,timSig)
if res~=0 then call readPacket
/* Timeout */
if and(sel.signals,timSig)=0 then
LAN.i.UP = 1
end
end
/* Do log entry
*/
line = date('S') ||'.'|| time() || ' |'
do i = 1 to LAN.0
if LAN.i.UP then
line = line || ' ' || left( (((LAN.i.avg/LAN.i.nrec)*100)%1)/100, 10 )
else
line = line || ' ' || left( 'Down', 10 )
end
if ~exists( logfile ) then
address command 'echo >LOGS:LANstatus.log'
if open('MyCon',logfile, 'a') then do
writeln( 'MyCon', line )
call close( 'MyCon' )
end
/* Delay for specified period, and check if it's time for hourly stats
*/
markhour = time( 'h' )
call delay( waitloop*50 )
Stats:
if markhour ~= time( 'h' ) | running = 0 then do
if ~exists( logfile ) then
address command 'echo >LOGS:LANstatus.log'
if open('MyCon',logfile, 'a') then do
writeln( 'MyCon', '' )
writeln( 'MyCon', 'Hourly network response statistics '|| date('e') ||
' ' || time() )
writeln( 'MyCon', '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' )
do i = 1 to LAN.0
if LAN.i.ntrans==0 then loss=100
else loss=(LAN.i.ntrans-LAN.i.nrec)/LAN.i.ntrans*100
line = left( LAN.i, 15 ) ||'- '|| LAN.i.ntrans 'packets transmitted,' LAN.i.nrec 'packets received,' loss'% packet loss.'
if LAN.i.nrec>0 then
line = line || ' round-trip min/avg/max =' LAN.i.min'/'|| (((LAN.i.avg/LAN.i.nrec)*100)%1)/100 || '/'LAN.i.max 'ms'
writeln( 'MyCon', line )
end
writeln( 'MyCon', '' )
call close( 'MyCon' )
end
if running then
line = date('S') ||'.'|| time() || ' |'
do i = 1 to LAN.0
LAN.i.min=1E10
LAN.i.max=0
LAN.i.avg=0
LAN.i.nrec=0
LAN.i.ntrans=0
if running then
line = line || ' ' || left( LAN.i, 10 )
end
if running then do
if ~exists( logfile ) then
address command 'echo >LOGS:LANstatus.log'
if open('MyCon',logfile, 'a') then do
writeln( 'MyCon', line )
call close( 'MyCon' )
end
end
if ~running then
brunning = 0
end /* hour/break stats */
if ~running then
signal ExitProgram
end /* loop while running */
ExitProgram:
exit
/* end of main loop */
err:
parse arg msg
say prg":" msg
exit
pad: PROCEDURE
parse arg string,len
return copies(d2c(0),len-length(string)) || string
d2m: PROCEDURE
parse arg val,len
return pad(d2c(val),len)
/* NOT procedure, so we can pick up variables from the main loop */
sendPacket:
parse arg dataSize
/** here we create an ICMP echo packet **/
pktf = ourID || d2m( LAN.i.ntrans, 2 )
call GetSysTime("TV")
pktf=pktf || d2m(tv.secs,4) || d2m(tv.micro,4) || copies("0"x,dataSize-8)
temp="0800"x || "0000"x || pktf
pkt="0800"x || d2c(InetCksum(temp)) || pktf
res=SendTo(sock,pkt,0,"SIN")
if res~=length(pkt) then do
e=errno()
if IsLibOn("TTCP") then es=e
else es=ErrorString(e)
LAN.i.UP = 0
end
LAN.i.ntrans=LAN.i.ntrans+1
return
/* NOT procedure, so we can pick up variables from the main loop */
readPacket:
/** here we read an ICMP packet and check if it is an ICMP
echo-replay for us **/
call GetSysTime("NOW")
res=RecvFrom(sock,"BUFF",256)
if res<0 then do
LAN.i.UP = 0
end
parse var buff vhl +1 tos +1 len +2 id +2 off +2 ttl +1 rest
hl=c2d(bitand(vhl,'F'x))*8
buff=c2x(buff)
parse var buff +hl type +2 code +2 cksum +4 id +4 seq +4 s +8 m +8 data
if c2d(len)~=dataSize+8 | type~='00' | code~='00' | id~=c2x(ourID)
then return
old.secs=x2d(s)
old.micro=x2d(m)
call SubTime("NOW","OLD")
time=now.secs*1000000+now.micro
time=time%1000"."time//1000
time=((time*100)%1)/100
LAN.i.nrec=LAN.i.nrec+1
if time>LAN.i.max then LAN.i.max=time
if time<LAN.i.min then LAN.i.min=time
LAN.i.avg=LAN.i.avg+time
return
/** statistics **/
BREAK_C:
if t~="T" then call StopTimer(t)
signal off BREAK_C
call close( 'MyCon' )
running = 0
signal Stats
--------------------------------------------------
Have fun!
--
Neil Williams,
neil@zeusdev.co.uk - ICQ UINs: 18223711 & 116110052
FidoNet 2:442/107.0 - Part time BBS: telnet:tkgbbs.darktech.org
--- Zeus BBS 1.5
# Origin: Killing^Ground . 22:00 - 08:00 . +44 [0]1978 751393 (2:442/107.0)
* Origin: LiveWire BBS - Synchronet - LiveWireBBS.com (1:2320/100)