/*
    DTUDPServ.rexx - daytime/udp server

    Shows:
    - how to write a mix inetd/stand-alone server
    - how to write an udp server

    Simple daytime/udp server.
    Run:
    - as stan-dalone:
        rx dtudpserv
      to stop:
        rx "shell DTUDPSERV quit"
    - as inetd service
        be sure you copied rxs to c:
        add to the services database:
            daytime 13/udp
        add to the inetd database:
            daytime dgram udp nowait root c:rxs rxs dtudpserv
        or
            daytime dgram udp nowait root c:rxs rxs "dtudpserv SYSLOG"
        if you want log

    Use daytimeudp.rexx as client.
*/

l="rexxsupport.library";if ~show("L",l) then;if ~addlib(l,0,-30) then do;say "can't find" l;exit;end
l="rmh.library";if ~show("L",l) then;if ~addlib(l,0,-30) then do;say "can't find" l;exit;end
l="rxsocket.library";if ~show("L",l) then;if ~addlib(l,0,-30) then do;say "can't find" l;exit;end

prg=ProgramName("NOEXT")

if ~RMH_ReadArgs("SYSLOG/S") then do
    call  PrintFault(IoErr(),prg)
    exit
end

log=parm.0.flag

ls=LastSocket()
if ls>=0 then do
    call handle(ls)
    exit
end

portName=upper(prg)
if ~OpenPort(portName) then call err "can't create port",1
ps=PortSignal(portName)

sock=socket("INET","DGRAM")
if sock<0 then call err "can't create socket"

local.addrFamily="INET"
local.addrAddr=0
if GetServByName("SE","daytime","udp") then
    local.AddrPort=se.ServPort
else local.AddrPort=13

if bind(sock,"LOCAL")<0 then call err "can't bind socket"

call info "opened"

sel.read.0=sock
open=1
do while open
    res=WaitSelect("SEL",,,or(ps,2**12))
    if res<0 then call err "wait error"
    if and(sel.signals,2**12)~=0 then open=0
    if res==1 then call handle(sock)
    if and(sel.signals,ps)~=0 then do
        pkt=GetPkt(portName)
        if pkt~=Null() then do
            comm=GetArg(pkt)
            res=0
            select
                when upper(comm)=="QUIT" then open = 0
                otherwise res=15
            end
            call Reply(pkt,res)
        end
    end
end
call info "closed"
exit

/***************************************************************************/
handle: procedure expose remote. log prg
parse arg sock
    if RecvFrom(sock,"dummy",1,,"REMOTE")<=0 then call info "recv error"
    call info "connection from" remote.AddrAddr":"remote.AddrPort
    call GetDate("D","GMT")

    d.0="Sun";d.1="Mon";d.2="Tue";d.3="Wed";d.4="Thu";d.5="Fri";d.6="Sat"
    m.1="Jan";m.2="Feb";m.3="Mar";m.4="Apr";m.5="May";m.6="Jun";m.7="Jul";m.8="Aug";m.9="Sep";m.10="Oct";m.11="Nov";m.12="Dec"

    all=formatdate("D","%m %w %d %Y %H:%M:%S GMT")
    parse var all i j rest
    i=i%1
    date=d.j"," m.i || rest"A"x
    if SendTo(sock,date,0,"REMOTE")<length(data) then call info "recv error"
    return
/***************************************************************************/
err: procedure expose log prg
parse arg msg,ntdoerr
    log=1
    call info msg "("Errorstring()")"
    exit

info: procedure expose log prg
parse arg msg
    if log then call SysLog(prg msg)
    say prg":" msg
    return
/***************************************************************************/
