12-24-2013 11:13 AM
I'm trying to create a simple process to monitor records being added to a file. I've simplified the code to show the problem.
$ PIPE TYPE/CONTINUOUS/INT=5 filename | @TEST
where TEST.COM is
$ READ/TIME_OUT=3/ERROR=ERR SYS$PIPE X
$ WRITE SYS$OUTPUT X
$ GOTO LOOP
$ WRITE SYS$OUTPUT "No input"
$ GOTO LOOP
When I issue the PIPE cmd, I see displayed the present contents of "filename", but when the end of the file is reached, the cmd hangs, as if the READ/TIME_OUT is not firing. I'm expecting to see the line "No input" appear every 3 seconds.
I've spent a lot of time on various versions of the code and did a GOOGLE search, but don't see why this won't work.
12-25-2013 01:13 AM
from memory, read/time_out will only work in a couple of cases:
1. reading from a terminal device
2. timeout for reading with a locked record
3. reading from a mailbox with timeout=0 to return immediate even if there's no data.
Since you're reading from a mailbox case 3 applies and only a zero timeout has a meaning.
So it does not work the way you would think.
12-26-2013 07:34 AM
Is the file writing process cooperating? Ideally it opened the file with full sharing.
That is open access=write, share=read (or more)
In the case you can just write a DCL poll-loop as the EOF will be live in the shared file lock (all transparent)
$ wait 0:0:3
$ read /end=my_wait file record
$ ! process record
$ goto loop
For a slightly less cooperative process you may need to open the file over and over to pick up the latest EOF.
DCL does not have tell/seek but several tools do, notably perl
So a quick perl program could
- get the last position (if any) from a symbol or logical, or command line argument
- open the file
- seek to last
- perhaps process the data or share out for a next step
- tell the last postion.
12-26-2013 02:47 PM
Put another way... /TIMEOUT just adds modifier SS$_TIMEOUT to the $QIO issed for the read. It will only work if the driver for the target device supports that modifier. The mailbox driver doesn't do timeouts. You can simulate them using "crossed ASTs". That is, you issue a timer AST which will $CANCEL on the input channel, you then issue a READ QIO on that channel, and on completion cancel the timer. Obviously not something you can do from DCL, but maybe with a small program reading the pipe?
If today is slow enough I'll see what I can come up with...
12-26-2013 05:18 PM
I think this does what you want. Running this batch job
mybatch.com $ SET VERIFY $ WRITE SYS$OUTPUT "Batch job running" $ loop: $ WAIT 00:00:01 $ WRITE SYS$OUTPUT "After 1 sec ",F$TIME() $ WAIT 00:00:01 $ WRITE SYS$OUTPUT "After 2 sec ",F$TIME() $ WAIT 00:00:10 $ WRITE SYS$OUTPUT "After 10 sec ",F$TIME() $ GOTO loop
$ pipe type/tail/cont/int=5 MYBATCH.LOG | @mywait
output looks like this
INPUT = "$ WAIT 00:00:01" INPUT = "$ WRITE SYS$OUTPUT "After 1 sec ",F$TIME()" INPUT = "After 1 sec 27-DEC-2013 11:42:10.36" INPUT = "$ WAIT 00:00:01" INPUT = "$ WRITE SYS$OUTPUT "After 2 sec ",F$TIME()" INPUT = "After 2 sec 27-DEC-2013 11:42:11.36" INPUT = "$ WAIT 00:00:10" Input timed out %X1000022C Input timed out %X1000022C Input timed out %X1000022C INPUT = "$ WRITE SYS$OUTPUT "After 10 sec ",F$TIME()" INPUT = "After 10 sec 27-DEC-2013 11:42:21.36" INPUT = "$ GOTO loop" INPUT = "$ loop:" INPUT = "$ WAIT 00:00:01" Input timed out %X1000022C INPUT = "$ WRITE SYS$OUTPUT "After 1 sec ",F$TIME()" INPUT = "After 1 sec 27-DEC-2013 11:42:22.36" INPUT = "$ WAIT 00:00:01" INPUT = "$ WRITE SYS$OUTPUT "After 2 sec ",F$TIME()" INPUT = "After 2 sec 27-DEC-2013 11:42:23.37" INPUT = "$ WAIT 00:00:10" Input timed out %X1000022C Input timed out %X1000022C Input timed out %X1000022C INPUT = "$ WRITE SYS$OUTPUT "After 10 sec ",F$TIME()" INPUT = "After 10 sec 27-DEC-2013 11:42:33.37" INPUT = "$ GOTO loop" INPUT = "$ loop:" INPUT = "$ WAIT 00:00:01" INPUT = "$ WRITE SYS$OUTPUT "After 1 sec ",F$TIME()" INPUT = "After 1 sec 27-DEC-2013 11:42:34.37" INPUT = "$ WAIT 00:00:01"
MYWAIT.COM looks like
$ SET NOON $ waitloop: $ MCR DISK_USERS:[GILLINGS_J]TIMEOUT INPUT 3 SYS$PIPE $ stat=$STATUS $ IF stat $ THEN $ SHOW SYM INPUT $ ELSE $ WRITE SYS$OUTPUT "Input timed out ''stat'" $ ENDIF $ GOTO waitloop
The TIMEOUT program syntax is
$ TIMEOUT <symbol> [<timeout in seconds> D=5] [<input device> D=SYS$PIPE] Reads input from the input device. If no input is seen within the timeout period, status SS$_TIMEOUT is returned (with SS$M_INHIB_MSG set) Default timeout is 5 seconds. Default input device is SYS$PIPE
As usual with this type of program, most of the work is in parsing and checking input parameters.
Written in MACRO32 so it compiles and links on all OpenVMS systems, regardless of compilers and licenses (and also because I like MACRO32 ;-) Invoke as a foreign command.
TIMEOUT.MAR .TITLE TimeoutRead $EFNDEF $DSCDEF $LIBDEF $SSDEF .PSECT data,rd,wrt,noexe,quad bintim: .QUAD IOSB: iostat: .WORD iochars:.WORD ioterm: .WORD iotrmsz:.WORD timeout: .LONG 5 ; seconds cvtop: .LONG LIB$K_DELTA_SECONDS stat: .LONG 0 Chan: .WORD DefTMO: .ASCID /5/ DefDev: .ASCID /SYS$PIPE/ Prompt: .ASCID /Symbol: / Blank: .ASCID // MaxLine=512 line_buf: .BLKB MaxLine input_buf:.BLKB MaxLine .MACRO Desc nam .ALIGN QUAD 'nam': 'nam'_len: .WORD 0 .BYTE DSC$K_DTYPE_T .BYTE DSC$K_CLASS_S 'nam'_ptr: .LONG .ENDM Desc .ALIGN QUAD Desc line Desc sym Desc TMO Desc Dev Desc input .PSECT code,rd,nowrt,exe .MACRO ParseToken,tok,def,err,?lab ; parses a space delimited token from the command line, ; or default value if line has been consumed. ; or error status if resulting string is null MOVAB def,R4 ; set output string to default value MOVW (R4),'tok'_len MOVL 4(R4),'tok'_ptr CMPL R2,R3 ; any more string to parse? BGEQ eos_'lab' MOVW #0,'tok'_len ; yes, start length at 0 MOVL R2,'tok'_ptr ; and string pointing to start next_'lab': CMPB (R2)+,#^A/ / ; look for a space BLEQ eos_'lab' ; reached end of string CMPL R2,R3 ; check for end of string BGTR eos_'lab' INCL 'tok'_len ; increment length BRB next_'lab' eos_'lab': TSTW 'tok'_len ; check that string is non-null BGTR ok_'lab' MOVL err,R0 RET ok_'lab': skip_'lab': CMPB (R2),#^A/ / ; step over any blanks BGTR eob_'lab' CMPL R2,R3 ; don't go past end of string BGEQ eob_'lab' INCL R2 BRB skip_'lab' eob_'lab': .ENDM ParseToken .PSECT code,rd,nowrt,exe .ENTRY canio,^M<> ; AST routine to cancel an I/O on input channel $CANCEL_S chan=chan MOVL #SS$_TIMEOUT,stat RET .ENTRY start,^M<R2,R3,R4,R5,R6,R7,R8> MOVAB line_buf,line_ptr ; set up descriptors for command line and input MOVW #MaxLine,line_len MOVAB input_buf,input_ptr MOVW #MaxLine,input_len PUSHAW line_len ; read and trim command line PUSHAB Prompt PUSHAB line CALLS #3,G^LIB$GET_FOREIGN PUSHAB line PUSHAB line CALLS #2,G^STR$TRIM MOVAB line_buf,R2 ; set start and end pointers for parsing string MOVZWL line_len,R3 ADDL2 R2,R3 ParseToken sym,Blank,#^X38080 ; get parameters ParseToken TMO,DefTMO,#20 ParseToken Dev,DefDev,#2312 PUSHAL timeout ; convert timeout parameter from string to binary seconds PUSHAB TMO CALLS #2,G^OTS$CVT_TI_L BLBC R0,Bad PUSHAB bintim ; convert binary seconds to delta time PUSHAB timeout PUSHAB cvtop CALLS #3,G^LIB$CVT_TO_INTERNAL_TIME BLBC R0,Bad PUSHAB Blank ; set symbol to null value in case of errors PUSHAB sym CALLS #2,G^LIB$SET_SYMBOL $ASSIGN_S devnam=dev chan=chan ; open I/O channel $SETIMR_S daytim=bintim astadr=canio reqidt=canio ; schedule timeout $QIOW_S efn=#EFN$C_ENF chan=Chan iosb=IOSB - ; read the input device func=#IO$_READVBLK p1=input_buf p2=#MaxLine BLBC R0,Bad ; check return and IOSB status values MOVZWL iostat,R0 BLBC R0,Bad $CANTIM_S reqidt=canio ; cancel the AST (not really necessary, but let's do the right thing) MOVW iochars,input_len ; set length of descriptor for input line PUSHAB input ; set symbol to input value PUSHAB sym CALLS #2,G^LIB$SET_SYMBOL Bad: BLBS R0,Good ; sort out return status. If success, just return it TSTL stat ; if stat is non-zero, the AST fired BEQL keep ; is zero, keep the existing R0 MOVL stat,R0 ; overwrite with stat keep: BISL #STS$M_INHIB_MSG,R0 ; set INHIBIT bit so DCL won't write an error message Good: RET .END Start
$ MACRO TIMEOUT
$ LINK TIMEOUT
(remember full filespec required for foreign commands)
$ $ ! valid input $ timeout testsym 5 sys$input some input $ show sym $status $STATUS == "%X00000001" $ show sym testsym TESTSYM = "some input" $ $ ! Allow it to timeout $ timeout testsym 5 sys$input $ show sym $status $STATUS == "%X1000022C" $ show sym testsym TESTSYM = ""
12-31-2013 09:11 AM
Thanks for the responses!
Looks like my original "simple" idea won't work!
Hein: I tried the first DCL solution you suggested, but nothing was displayed after new records were appended to the original file. I guess that whatever mode the file is being opened in, the EOF marker is not getting updated somewhere where the DCL can see it. (I don't know what parameters the third party product is using to open the file to append records. I can see that the file doesn't have a channel open so the app must just open it when records need to be added.)
John: thanks very much for going out of you way to provide a Macro solution! Interesting reading for me, since I haven't used Macro in a way long time! Unfortunately, this solution seems too complex for what I had hoped could be solved with some DCL.