; USR0:RWHODS.MAC.44 6-May-86 LQ+5D.3H.36M.47S., by BUDD ; Flush domains on input, don't send out info on detached jobs ; USR0:RWHOD.MAC.14 16-Aug-85 NM+10H.56M.4S., by BUDD ; Try Job idle time if TTY idle time fails ; USR0:RWHOD.MAC.2 14-Jun-85 LQ+3D.21H.18M.5S., by BUDD ; Use UDP: for output ; USR0:RWHODR.MAC.2 9-Jun-85 FM+7D.4H.46M.17S., by BUDD ; Quick hack to use UDP: device for read ; USR0:RWHOD.MAC.12 18-May-85 LQ+6D.23H.35M.23S., by BUDD ; We now have IDLE% ; USR0:RWHOD.MAC.3 26-Mar-85 NM+5D.20H.42M.7S., by BUDD ; ADD IFDEF IDLE% FOR BUCS20 -- MUST GET IDLE TIME SOMEHOW. ;RWHOD.MAC.38, 31-Jan-85 20:11:40, Edit by SATZ ; ignore sndin errors ;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%. ;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 ;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 ;RWHOD.MAC.15, 21-Oct-84 21:49:05, Edit by SATZ ; read the NCTs from the monitor to determine broadcast addresses ;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 ;RWHOD.MAC.5, 13-Oct-84 19:19:57, Edit by LOUGHEED ;RWHOD.MAC.3, 13-Oct-84 19:10:09, Edit by LOUGHEED ; Write eight bit files ;RWHOD.MAC.119, 12-Oct-84 11:39:30, Edit by LOUGHEED ; Try to make the main loop more efficient title rwhodS 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=>>+1>>+1>>+1>>+1> Q3=>+1>+1 P6=+1>+1>+1>+1>+1 P=>+1 STANSW==0 Define Errmsg (Msg) < jrst [ perstr(rwhod: Msg - ) Jrst Done] > subttl definitions rwhopt==^d513 ; listen port ;slptim==^d<1000*60*5> ; wait on 5 minute intervals slptim==^d<1000*60> ; wait on 1 minute intervals 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 nctsiz==^d6 ; first 6 words of the NCT IFN STANSW,< NT.MEI==:4 ; Device type 4, MEIS >;IFN STANSW IFE STANSW,< NT.NIP==:1 ; (Rel 5.4) Device 1 = NI-IP > ;IFE STANSW 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 prgnam: asciz /rwhod/ whodir: asciz /system:rwhod.data/ ; mapped data file udphst: asciz /system:hosts.udp/ ; systems to send rwho packets subttl storage pdllen==200 pdl: block pdllen UDPJFN: BLOCK 1 ; UDP: JFN UDPBLK: 4 ; UDP MTOPR BLOCK BLOCK 3 ; ... rcvdat: block maxpkt+1 ; receive packet data snddat: block maxpkt+1 ; send packet data debug: block 1 ; debugging flag buffer: block bufsiz ; temp. string space buf0: block bufsiz ; this one is for interrupt routine 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 hosts: block nhosts ; place to put addresses to send to jobs: block .jimax ; place to hold job info njobs: block 1 ; number of jobs on the system nct: block nctsiz ; get first six words of NCT acs: block 20 ; place to save all of the acs txtblk: .rddbc ; texti% argument block -- count rd%crf!rd%jfn!rd%bel ; flags 0,,.nulio ; hosts.udp,,nul: point 7,buffer ; put input here bufsiz*5 ; number of characters lev1pc: block 1 lev2pc: block 1 lev3pc: block 1 levtab: lev1pc ; interrupt level table lev2pc lev3pc chntab: 3,,tmrint ; handle timer interrupts tmrchn==.-chntab-1 block 35 ; unused channels subttl main routine rwhod: reset% ; the world move p,[iowd pdllen, pdl] ; set up the stack movei t1,.fhslf ; get our privs. rpcap% seto t3, ; enable all we can epcap% seto t1, ; release all queues reliq% jfcl ; ignore errors here hrroi t1,.jobrt ; find max. number of jobs getab% ; on the system. errmsg (getab% failure) ; bummer movmm t1,njobs call config ; get network and host info skipn ihost ; do we have anything to send to? errmsg (nobody to talk to) ; no, don't bother then call mapfil ; map in the data file movei t1,.fhslf ; our process move t2,[levtab,,chntab] ; table addresses sir% ; set interrupts movx t2,1b ; enable it aic% eir% ; enable them ;;; dmove t1,prgnam ; our program name movei t1,.gthsz ; Want table sizes + local host gthst% ; Get it errmsg (Could not get local host number); barf movei t1,.gthns ; convert from number to string hrroi t2,buffer ; temp. store in here move t3,T4 gthst% ; get our primary name IFE STANSW,< errmsg (gthst% failed on our own name) ; huh >;IFE STANSW IFN STANSW,< ifjer. hrroi t1,buffer ; use PUP to determine name, if we can ldb t2,[point 8,lclhst,19] ; get PUP network number ldb t3,[point 8,lclhst,35] ; and the host number hrl t3,t2 ; put net in LH movx t2,pn%fld!pn%oct!t3 setz t4, pupnm% ; do it errmsg (can't get my own name; who am i?) endif. >;IFN STANSW move t1,[point 7,buffer] ; get rid of that domain call strdom ; do it move t1,[point 7,buffer] ; convert to lower case call lower ; do it movei q1,snddat ; set up host name in sending packet move t1,[point 8,wdhost(q1)] ; copy to here move t2,[point 7,buffer] ; from here movei t3,^d32 ; for only 32 characters call strncp ; do it 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 MOVEI T1,.FHSLF ; THIS FORK MOVX T2,1B ; TIMER CHANNEL IIC% ; START OUTPUT TASK 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 timer interrupt tmrint: movem p,acs+17 ; save stack ptr here movei p,acs ; destination blt p,acs+16 ; save rest of acs move p,acs+17 ; get stack pointer back move t1,[.fhslf,,.timal] ; clear all interrupt requests timer% ; do it errmsg(timer% clear failure) ; opppps movei p1,snddat ; get address of send data movei p2,wduser(p1) ; ptr to user data setz q1, ; job counter tmrin0: camle q1,njobs ; for each job jrst tmrin2 ; reached limit, send packet move t1,q1 ; get job number to look at move t2,[-.jimax,,jobs] ; get everything movei t3,.jijno ; this offset getji% ; get job info erjmp tmrin1 ; no such animal skipn t2,jobs+.jiuno ; ignore not-logged-in jrst tmrin1 ; for now hrroi t1,buf0 ; temp. buffer will dirst% ; put username out erjmp tmrin1 ; skip it entirely hrroi t1,buf0 ; compare what we found hrroi t2,[asciz/operator/] ; with this guy stcmp% ; compare them jumpe t1,tmrin1 ; ignore operator jobs move t1,[point 7,buf0] ; force string call lower ; to lower case move t1,[point 8,whname(p2)] ; copy the username into here move t2,[point 7,buf0] ; from here while movei t3,^d8 ; only eight letters call strncp ; do it move t1,[point 8,whline(p2)] ; where line info goes move t2,[point 7,[asciz/tty/]] ; insert tty name call strcpy ; copy skipl t2,jobs+.jitno ; get terminal number ifskp. ; detached JRST TMRIN1 ;[BUDD] ;; move t2,[point 7,[asciz/det/]] ; say so ;; call strcpy ; copy that in else. movei t3,^d8 ; output octal number nout ; output number erjmp .+1 ; ignore this endif. move t1,jobs+.jistm ; get logged in time call t2utim ; convert to unix time stor t1,whtime,(p2) ; save into packet move t1,q1 ; get job number back IFNDEF IDLE%,< OPDEF IDLE% [JSYS 701] ID%TTY==1,,0 > ;IDLE% txo t1,id%tty ; want tty idle time idle% ; get it IFJER. ; [BUDD] MOVE T1,Q1 ; [BUDD] IDLE% ; [BUDD] TRY JOB IDLE TIME IFJER. ; [BUDD] SETZ T1, ; [BUDD] ENDIF. ; [BUDD] ENDIF. ; [BUDD] idivi t1,^d1000 ; convert to seconds idle time stor t1,whidle,(p2) ; save it in packet addi p2,usrsiz/4 ; increment user packet data ptr tmrin1: aos q1 ; bump job counter jrst tmrin0 ; check next job tmrin2: time% ; get uptime idiv t1,t2 ; make seconds move t2,t1 ; save uptime here gtad% ; get time and date call t2utim ; convert to unix time sub t1,t2 ; make seconds uptime stor t1,wdboot,(p1) ; save it in packet movei t1,wdver ; get rwhod version stor t1,wdvers,(p1) ; save it in packet too movei t1,wdtyp ; get rwhod type stor t1,wdtype,(p1) ; ditto for this one move t1,[14,,.systat] ; get 1 minute load average getab% erjmp .+1 ; ignore errors fmpr t1,[100.0] ; multiply by 100 fixr t1,t1 ; make it an interger stor t1,wdld1,(p1) ; save it erjmp .+1 ; continue if error move t1,[15,,.systat] ; get 1 minute load average getab% erjmp .+1 ; continue if error fmpr t1,[100.0] ; multiply by 100 fixr t1,t1 ; make it an interger stor t1,wdld5,(p1) ; save it move t1,[16,,.systat] ; get 1 minute load average getab% erjmp .+1 ; continue if error fmpr t1,[100.0] ; multiply by 100 fixr t1,t1 ; make it an interger stor t1,wdldf,(p1) ; save it gtad% ; get current time call t2utim ; conver to unix time stor t1,wdsend,(p1) ; save in packet setz q1, ; host counter subi p2,snddat ; get number of words in packet imuli p2,4 ; make 8 bit bytes tmrin3: caml q1,ihost ; for each host jrst tmrin4 ; done, restore acs and return MOVE T2,HOSTS(Q1) ; GET ADDRESS OF HOST TO SEND TO MOVEM T2,UDPBLK+.UDADR ; STORE XMOVEI T2,SNDDAT ; GET BUFFER MOVEM T2,UDPBLK+.UDBUF ; STORE MOVEM P2,UDPBLK+.UDSIZ ; STORE LENGTH MOVE T1,UDPJFN ; GET JFN MOVEI T2,'UOU' ; UDP OUTPUT FUNCTION MOVEI T3,UDPBLK MTOPR ; DO SEND! ERJMP .+1 ; AWWW! aoja q1,tmrin3 ; do another host tmrin4: move t1,[.fhslf,,.timel] ; reset the timer movx t2,slptim ; so wake up in slptim seconds movx t3,tmrchn ; wake up on this channel timer% ; do it errmsg (timer% screwup) ; say what happened hrlzi p,acs ; move back to acs blt p,cx ; up to 16 move p,acs+17 ; restore stack ptr debrk% ; continue from interrupt subttl remove domains from host names ; t1/bp to host string strdom: push p,t2 ; save a register push p,t3 ; and another one strdo0: ildb t2,t1 ; get next character jumpe t2,strdo1 ; null found, done caie t2,"." ; is it domain marker? jrst strdo0 ; no, try again setz t3, ; drop a null on it dpb t3,t1 strdo1: pop p,t3 ; restore acs pop p,t2 ret subttl lower case a string in place ; t1/bp to destination lower: push p,t2 ; save a register lower0: ildb t2,t1 ; get a character jumpe t2,lower1 ; done on null cail t2,"A" ; is it alpha caile t2,"Z" ; and upper skipa ; no, forget it txo t2,40 ; force lower dpb t2,t1 ; deposit it back jrst lower0 ; and loop lower1: pop p,t2 ; get t2 back ret ; done 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 configure hosts config: setzm ihost ; zero host counter ; read in the ncts from the monitor to determine what interfaces we have movx t1,.snpsy ; snoop a symbol move t2,[radix50 0,.NCTS] ; get first NCT (0) move t3,[radix50 0,STG] ; it better not move snoop% erjmp confg3 ; assume no such symbol confg0: movsi t1,nctsiz ; get this many words hrr t1,t2 ; get monitor address movei t2,nct ; put stuff here peek% ; erjmp confg3 ; quit on error hrrz t2,nct+1 ; get device code IFN STANSW,< cain t2,nt.mei ; meis can do broadcast at stanford jrst confg2 ; figure out broadcast address >;IFN STANSW ;IFE STANSW,< ; CAIN T2,NT.NIP ; JRST CONFG2 ;>;IFE STANSW confg1: hrrz t2,nct ; get link word to next NCT jumpn t2,confg0 ; do another if non-zero jrst confg3 ; done here, check out file ; found an interface with broadcast ability confg2: move t1,nct+3 ; get interface network number movx t2,377 ; assume class C caig t1,177777 ; class A or B? movx t2,177777 ; assume class B caig t1,377 ; class A? movx t2,77777777 ; set it so IFN STANSW,< lsh t2,-8 ; move over for the subnet field >;IFN STANSW move t1,nct+4 ; get interface address ior t1,t2 ; make a broadcast address move t2,ihost ; get host counter movem t1,hosts(t2) ; save it aos t2 ; increment counter cail t2,nhosts ; check for limit jrst confi1 ; reached it, finish up movem t2,ihost ; save new value jrst confg1 ; check next NCT ; try to read in the file system:hosts.udp for others to send to confg3: movx t1,gj%sht!gj%old ; old file hrroi t2,udphst ; open this file gtjfn% ; can we get at it? erjmp r ; no, then don't bother hrrzs t1 ; just jfn movx t2,of%rd!fld(7,of%bsz) ; open read openf% ; try then open erjmp r ; forget it then push p,t1 ; save jfn confi0: move t2,0(p) ; get jfn back movei t1,txtblk ; get texti% block hrlm t2,.rdioj(t1) ; stuff input jfn in block move t2,[point 7,buffer] ; get ptr to string space movem t2,.rddbp(t1) ; save it in block movei t2,bufsiz*5 ; get string length movem t2,.rddbc(t1) ; save it texti% ; read it in erjmp confi1 ; finished reading movei t1,.gthsn ; string to number hrroi t2,buffer ; from here gthst% ; translate erjmp [move t1,[point 7,buffer];get ptr back to host buffer ildb t2,t1 ; get the first character cail t2,"0" ; is it in the number range caile t2,"9" ; since it may be host number jrst confi0 ; not a number, ignore it move t1,[point 7,buffer];get ptr again movei t3,^d10 ; get decimal nin% ; read in number erjmp confi0 ; ignore it if error move t4,t2 ; get number in here movei q1,3 ; set up for loop conf01: nin% ; read in next number erjmp confi0 ; ignore it if error imuli t4,^d256 ; shift left 8 bits add t4,t2 ; move in next byte sojn q1,conf01 ; do it again move t3,t4 ; put host number here jrst .+1] ; continue move t1,ihost ; get host counter movem t3,hosts(t1) ; save host number aos t1 ; bump counter cail t1,nhosts ; under the limit jrst confi1 ; no, finish up movem t1,ihost ; save new value jrst confi0 ; do another confi1: pop p,t1 ; get jfn back closf% ; close up the file erjmp .+1 ret ; done, return 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 caie t2, "." ;[budd] 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 DO. ;[BUDD] FLUSH DOMAINS... ILDB T3, T2 CAIN T3, "." SETZ T3, IDPB T3, T1 JUMPN T3, TOP. OD. ;[BUDD] ;;; 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 rwhod jrst rwhod vernum: b2+b11+b17+b35 end <3,,entvec> ;* Local Modes: * ;* Comment Column:40 * ;* Comment Start:"; " * ;* End: *