; USR0:<BUDD.UDP>RWHODR.MAC.2  9-Jun-85 FM+7D.4H.46M.17S., by BUDD
;  Quick hack to use UDP: device for read
; USR0:<BUDD.RWHO>RWHOD.MAC.12 18-May-85 LQ+6D.23H.35M.23S., by BUDD
;  We now have IDLE%
; USR0:<BUDD.RWHO>RWHOD.MAC.3 26-Mar-85 NM+5D.20H.42M.7S., by BUDD
;  ADD IFDEF IDLE% FOR BUCS20 -- MUST GET IDLE TIME SOMEHOW.


;<TCP>RWHOD.MAC.38, 31-Jan-85 20:11:40, Edit by SATZ
; ignore sndin errors
;<TCP>RWHOD.MAC.32,  4-Nov-84 02:08:26, Edit by SATZ
; Table format contains byte count in RH instead of page number.
; Word zero of packet data is now first word of packet data in the page.
; The host name is now stored in ASCIZ for tbxxx%.
;<TCP>RWHOD.MAC.21,  4-Nov-84 01:20:51, Edit by SATZ
; Make rwhod read data into one single mapped data file. The format
; of the file is: page 1: index page in tbxxx% format
;		  page 2-nhosts: word 0 is byte count
;				 word 1-511 is packet data
;<TCP>RWHOD.MAC.16, 21-Oct-84 22:02:05, Edit by SATZ
; when getting packet set size to be in 8 bit octets not 7 bit bytes
;<TCP>RWHOD.MAC.15, 21-Oct-84 21:49:05, Edit by SATZ
; read the NCTs from the monitor to determine broadcast addresses
;<TCP>RWHOD.MAC.6, 21-Oct-84 18:47:11, Edit by SATZ
; Use PUPNM if GTHST fails
; Add string copy routine to replace jsys calls
;<TCP>RWHOD.MAC.5, 13-Oct-84 19:19:57, Edit by LOUGHEED
;<TCP>RWHOD.MAC.3, 13-Oct-84 19:10:09, Edit by LOUGHEED
; Write eight bit files 
;<SATZ.MACRO>RWHOD.MAC.119, 12-Oct-84 11:39:30, Edit by LOUGHEED
; Try to make the main loop more efficient

	title	rwhodR
	subttl	introduction

comment	|
	Greg Satz, SRI International, July, 1984

	This program will process the Unix rwho protocol. This is
	an unofficial data passing protocol that passes load and usage
	information amoung many machines on a network.
	|


	subttl	libraries, constants, and macros

	search monsym
	search macsym
	.requir sys:macrel
	sall

	veredt==1
	vermaj==1
	vermin==1
	vercst==20

	AC4=<T4=<AC3=<T3=<AC2=<T2=<AC1=<T1=<AC0=<F=<T0=0>>>+1>>+1>>+1>>+1>
	Q3=<Q2=<Q1=<T5=5>>+1>+1
	P6=<P5=<P4=<P3=<P2=<P1=10>+1>+1>+1>+1>+1
	P=<Q=<CX=16>>+1

Define	Errmsg	(Msg<What:>) <
	jrst [  perstr(rwhod: Msg - )
		Jrst Done]
>

	subttl	definitions

datpag==20000				; address to start saving data
nhosts==^d100				; up to 100 hosts (input and output)
usrsiz==^d24				; data size for each user
maxpkt==^d512				; maximum packet size
bufsiz==20				; string size

wdver==1
wdtyp==1				; host status

;rwhod packet structure
defstr wdvers,0,7,8			; version
defstr wdtype,0,15,8			; type
defstr wdsend,1,31,32			; send time
defstr wdrecv,2,31,32			; receive time
wdhost==3				; hostname, 32 characters
defstr wdld1,13,31,32			; 1 minute load avg.
defstr wdld5,14,31,32			; 5 minute load avg.
defstr wdldf,15,31,32			; 15 minute load avg.
defstr wdboot,16,31,32			; boot time
wduser==17				; user/who info

whline==0				; line, 8 chars
whname==2				; username, 8 chars
defstr whtime,4,31,32			; login time
defstr whidle,5,31,32			; idle time

whodir:	asciz /system:rwhod.data/	; mapped data file

	subttl	storage

pdllen==200
UDPJFN:	BLOCK 1				; UDP JFN
pdl:	block pdllen
rcvdat:	block maxpkt+1			; receive packet data
debug:	block 1				; debugging flag
buffer:	block bufsiz			; temp. string space
dtrlen:	block 1				; recevied packet data size
dtslen:	block 1				; send packet data size
ihost:	block 1				; host counter for hosts we send to
ohost:	block 1				; host counter of hosts we receive
datjfn:	block 1				; where to put the data file jfn
	subttl	main routine

rwhodR:	reset%				; the world
	move p,[iowd pdllen, pdl]	; set up the stack
	movei t1,.fhslf
	seto t3,			; enable all we can
	epcap%
	call mapfil			; map in the data file

	MOVSI T1,(GJ%SHT)
	HRROI T2,[Asciz "UDP:513#.513#"]
	GTJFN
	 ERRMSG (GTJFN failed)
	MOVEM T1,UDPJFN
	MOVE T2,[100000,,OF%RD+OF%WR]	; 8 bit bytes
	OPENF
	 errmsg (Open failed)		; say why

	subttl	packet receive loop

getpkt:
	MOVE T1,UDPJFN			; GET INPUT JFN
	MOVE T2,[POINT 8,RCVDAT]	; BUFFER
	MOVNI T3,MAXPKT*4		; how much to get (octets)
	SINR
	 ERJMP [errmsg (UDP input error) ; no, bad packet
		jrst getpkt]
	ADDI T3,MAXPKT*4		; how much to get (octets)
	movem t3,dtrlen			; save packet length (octets)
	call valdte			; validate it and then
	 call proces			; process the packet
	jrst getpkt			; do some more

	subttl tops-20 to unix time routine

; t1/tops-20 time format
; returns +1/ always with number of seconds in t1

t2utim:	push p,t2			; save t2
	push p,t3			; and t3
	sub t1,[^D40587,,0]		; # days between t20 and unix day 0
	hlrz t3,t1			; get day count in t3
	imuli t3,^D<3600*24>		; multiply by seconds in a day
	hrrzs t1			; isolate fraction of day
	imuli t1,^D<3600*24>		; get seconds
	hlrz t2,t1			; get seconds in t2
	txne t1,400000			; round up to nearest second
	 aos t2				; add it in
	add t3,t2			; total seconds in t3
	movem t3,t1			; return seconds here
	pop p,t3			; restore t3
	pop p,t2			; ditto
	ret				; all done

	subttl map in data file

mapfil:	movx t1,gj%sht!gj%old!1b35	; always want generation 1
	hrroi t2,whodir			; of this file
	gtjfn%
	 ifjer.				; not found
	   caie t1,gjfx19		; "no such file type"?
	    cain t1,gjfx24		; "file not found"?
	     skipa
	      errmsg (can't gtjfn% data file)
	   movx t1,gj%sht!gj%new!1b35	; make a new file then
	   hrroi t2,whodir
	   gtjfn%
	    errmsg (can't create data file)
	   hrli t1,.FBPRT		; make protection readable
	   movei t2,-1			; mask to change
	   movei t3,775252		; to this protection
	   chfdb%
	    erjmp .+1			; don't bother
	 endif.
	hrrzs t1			; just jfn
	movem t1,datjfn			; save it away
	movx t2,of%rd!of%wr!of%thw	; open it thawed
	openf%
	 errmsg (couldn't openf% data file)
	hrlzs t1			; jfn,,page number (0)
	move t2,[.fhslf,,datpag_-9]	; my process, starting at datpag
	movx t3,pm%cnt!pm%rd!pm%wr!nhosts+1 ; total hosts plus index page
	pmap%
	skipe datpag			; first time opening the file
	 ifskp.				; yes, setup table word correctly
	  movei t1,777			; size of page (number of entries)
	  movem t1,datpag		; save 0,,777 as tbxxx% head word
	  setzm ohost			; seen zero hosts
	 else.				; data file contains something
	  hlrz t1,datpag		; get tbxxx% count half word
	  movem t1,ohost		; use as initial host counter
	 endif.
	ret				; done

	subttl process the packet

proces:	gtad%				; get receive time
	call t2utim			; convert to unix format
	movei p1,rcvdat			; ptr to pkt data
	stor t1,wdrecv,(p1)		; store it in the packet
	call cnvhst			; have to convert hostname to asciz
	movei t1,datpag			; get table header word address
	move t2,[point 7,wdhost(p1)]	; ptr to converted hostname
	tbluk%				; have we seen this one already
	ifxe. t2,tl%exm			; exact match means in table
	  move t2,ohost			; no, get host counter
	  cail t2,nhosts		; reached maximum we can handle
	   ret				; then return without further ado
	  aos t2			; move it up
	else.
	  move t2,dtrlen		; get byte count
	  hrrm t2,(t1)			; update it with new value
	  hlrz t2,(t1)			; get table entry address
	  lsh t2,-9			; make it a page number
	  subi t2,datpag_-9
	endif.
	move t3,t2			; get copy of page count offset
	imuli t3,1000			; turn into page address offset
	move t4,t3			; get copy of page address offset
	hrli t1,rcvdat			; copy from packet
	hrri t1,datpag(t4)		; to offset page
	blt t1,datpag+777(t4)		; to right page, indexed
	camg t2,ohost			; did this create a new page?
	 ifskp.				; yes, then must update table
	   movem t2,ohost		; remember new maximum
	   addi t4,datpag		; point to newly copied packet
	   movei t1,datpag		; prepare to add it into table
	   hrli t2,wdhost(t4)		; should point to host name
	   hrr t2,dtrlen		; get byte count
	   tbadd%
	 endif.
	ret				; and return

	subttl validate packet

; returns +1 if legit.
; returns +2 if illegal packet
valdte:	movei p1,rcvdat			; get data
	load t1,wdvers,(p1)		; get the version
	caie t1,wdver			; is it the correct version?
	 retskp				; no, skip it
	load t1,wdtype,(p1)		; get type
	caie t1,wdtyp			; is it the right one?
	 retskp				; nope
	move t1,[point 8,wdhost(p1)]	; get ptr to host name
	call verify			; make sure name is legit.
	 retskp				; nope
	ret				; go back

	subttl	verify hostname

; t1/bp to hostname
; returns +1/ hostname has illegal characters
;	  +2/ hostname is okay
verify:	ildb t2,t1			; get first character
	jumpe t2,r			; null. bad.
verif0:	cail t2,"a"			; check for alpha lower
	 caile t2,"z"
	  skipa
	   jrst verif1
	cail t2,"0"			; check for digit
	 caile t2,"9"
	  skipa
	   jrst verif1
	cain t2,"-"			; check for this one
	 jrst verif1			; ok, check next one
	cail t2,"A"			; alpha lower
	 caile t2,"Z"
	  ret				; no good, quit
verif1:	ildb t2,t1			; get next byte
	jumpn t2,verif0			; not end, try verify another
	retskp				; skip return

	subttl	convert hostname from 8 bit string to asciz

cnvhst:	move t1,[point 7,buffer]	; copy it to here
	move t2,[point 8,wdhost(p1)]	; p1 set in proces
	movei t3,^d32			; for at most this many bytes
	call strncp			; convert from 8 to 7
	move t1,[point 7,wdhost(p1)]	; copy it back to destination
	move t2,[point 7,buffer]	; from temp. buffer
	call strcpy			; will always be less then original
	ret				; done

	subttl	string copy routines

; t1/ bp to copy to
; t2/ bp to copy from
strcpy:	ildb t3,t2			; get a byte
	idpb t3,t1			; store it
	jumpn t3,strcpy			; loop till null
strcp0:	seto t3,			; back up over null
	adjbp t3,t1			;
	exch t1,t3			; put result back in t1
	ret				; done

; t1/ bp to copy to
; t2/ bp to copy from
; t3/ count
strncp:	jumpe t3,r			; return when count is 0
	ildb t4,t2			; get a byte
	idpb t4,t1			; store it
	jumpe t4,strcp0			; done on null, back up over it
	soja t3,strncp			; loop back for another

	subttl	finish up

finish:	MOVE	T1,UDPJFN
	CLOSF
	 jfcl				; ignore things here

done:	haltf%
	jrst .-1

	Subttl	entry vector

entvec:	jrst rwhodR
	jrst rwhodR
vernum:	<veredt>b2+<vermaj>b11+<vermin>b17+<vercst>b35

	end	<3,,entvec>

;* Local Modes: *
;* Comment Column:40 *
;* Comment Start:"; " *
;* End: *
 