I gave this some thought and have a kludgy solution using three separate files:
Code: Select all=== DEFAULT.BAS ==
' Put program and variable initialization here.
let safe_remote = "123.45.67.89:4568"; ' Might read this from a file instead after pulling it from a safe web server
udpbegin 4567
load "main.bas"
wait
=== End of DEFAULT.BAS ===
=== MAIN.BAS ===
udpbranch [UDPreceived]
let execute_cmd = 0
do
' Do whatever else you want the main program to do.
' put an optional delay here
while execute_cmd = 0
load "/data/executable.dat"
wait
[UDPreceived]
let newcode = udpread()
' Check to see that command came from a trusted source and bail out if not
if udpremote <> safe_remote then return
' Build EXECUTABLE.BAS from the received command line plus a line to reload MAIN.BAS when complete.
let newcode = newcode & chr(13) & "load " & chr(34) & "main.bas" & chr(34) & chr(13)
write("executable", newcode)
let execute_cmd = 1
return
=== End of MAIN.BAS ===
Theory: The DEFAULT.BAS program handles initialization and then runs the meat of the program found in MAIN.BAS. If a command is received via UDP, the source IP is checked for safety. If it passes, the command is written to EXECUTABLE.DAT along with a
load "main.bas" line to return control to the main procedure where the usual fun and UDP listening happens. After the executable file is written, a flag is set to allow the executable to run. The actual execution is not done in the UDP interrupt to avoid a stack overflow. Once control returns, the main loop drops out, the EXECUTABLE.DAT file is run, then MAIN.BAS is reloaded and run.
I see one flaw: If a UDP packet is received while EXECUTABLE.DAT is running, an error will be thrown because the interrupt routine will not be present. This can be avoided by stopping UDP listening before launching EXECUTABLE.BAS and restarted in MAIN.BAS but there is a risk of missing UDP messages altogether.