home *** CD-ROM | disk | FTP | other *** search
- /*rx
- * at.rexx --- simulate UNIX at command. For use with CyberCron.
- * By Loren J. Rittle (rittle@comm.mot.com) - Tue May 5 01:47:11 1992
- * Updated to work with any AmigaOS shell - Wed May 6 03:32:00 1992
- *
- * Copyright ⌐ 1992 Loren J. Rittle
- * Use as you will, just document your changes and keep my copyright
- * notice intact. Feel free to mail enhancements to me.
- *
- * Modifications ⌐ 1992 by Graham Walter (gwalter@gwalter.demon.co.uk)
- * Following added:
- * Ability to specify date, incremental time periods
- * Remove (-r option) jobs
- * List (-l option)
- * Schedule (-s option) - used to reschedule jobs if
- * CyberCron is restarted (due to eg reboot)
- *
- * Usage:
- * at [-q[ac-z]] time [day [month [year]] [+ increment timeperiod]
- * <shell script>
- * <EOF>
- *
- * at -qb
- * <shell script>
- * <EOF>
- *
- * at -l [<task name as given at issue time, or from list>]
- * [for list current 'at' job(s)]
- *
- * at -r <task name as given at issue time, or from list>
- * [remove an 'at' job as shown in list]
- *
- * at -s
- * [schedule any unscheduled jobs]
- */
-
- /* USER MODIFIABLE DEFAULT, EDIT TO TASTE */
-
- /* AT_FILES should be somewhere that only at writes to.
- T: is not a suitable location, IMHO. In the future, at.rexx
- and some startup code magic might know how to requeue jobs
- that should have been run while power was off or after a crash
- occurred. To prepare for this, AT_FILES should be on a real
- file system. */
- AT_FILES = 's:at/'
- CRONLOG = "T:CronLog"
-
- /* NO GENERAL USER MODIFIABLE PARTS BELOW THIS COMMENT. */
-
- /* Default queue to place jobs in. Remember queue 'b' is special. */
- QUEUE = 'a'
-
- HOUR = '00'
- MINUTE = '00'
-
- options results
-
- parse arg Parameters
- parse arg option line xline
-
- if left( option, 2 ) = "-l"
- then do
- call ListAt line
- exit
- end
-
- if left( option, 2 ) = "-r"
- then do
- call RemoveAt line
- exit
- end
-
- if left( option, 2 ) = "-s"
- then do
- call ReSchedule line
- exit
- end
-
- call ParseParameters Parameters
-
- do 100 until ~ exists( AT_FILES || ID )
- ID = GenerateId()
- end
-
- if exists( AT_FILES || ID )
- then call ErrorExit 'at: can''t find unused ID'
-
- scriptfile = AT_FILES || ID
-
- if ~open('sfh', scriptfile, 'W')
- then call ErrorExit 'at: can''t open' scriptfile 'for output'
-
- call writeln('sfh', '; ****** Start of Prologue *******')
-
- call writeln('sfh', '; ****** At Job' ID ' *******')
-
- call writeln('sfh', 'cd' '22'x||pragma('D')||'22'x)
- call writeln('sfh', 'stack' pragma('S', 4000))
-
- if left(address(), 4) == 'WSH_'
- then do
- /*
- * If we have a WShell under us, then try to propagate
- * local environment variables. If you don't, I'm not
- * sorry for you, read comments below. :-)
- */
- 'set | execio stem VARS.'
- do i = 1 to VARS.0
- if find( "PROCESS RC RESULT2", upper( word(VARS.i, 1) ) ) = 0
- then call writeln('sfh', 'set' VARS.i)
- end
- end
- else say 'warning: WShell not present under your ARexx, local environment variables can''t be propagated'
-
- call writeln('sfh', '; ******** End of Prologue *******')
-
- line = readln( stdin )
-
- do while ~ eof( stdin )
- call writeln('sfh', line)
- line = readln(stdin)
- end
-
- call writeln('sfh', '; ****** Start of Epilogue *******')
- call writeln('sfh', 'run <nil: >nil: delete' scriptfile)
- call close('sfh')
-
- address command 'protect' scriptfile '+s'
-
- USER = getenv('USER')
- if left(address(), 4) == 'WSH_' then
- do
- /*
- * If we have a WShell under us, then try to get a local
- * environment variable with the name USER to override
- * the global USER setting.
- * If you don't have a WShell under you, then why not? :-)
- * Basically, you are less than human if you don't have a WShell
- * under you. If everyone had a WShell under their ARexx,
- * I wouldn't have to write such weird ARexx code. I hate you
- * if you don't WShell under your ARexx, cause you make my life
- * Hell! Does anyone read these comments, or I'm I wasting
- * my time here? :-)
- */
- /* AmigaDOG braindamage: */
- 'get USER >nil:'
- if rc == 0 then
- 'get USER | execio var USERX'
- else
- USERX = ''
- if USERX ~= '' then
- USER = USERX
- end
- else
- say 'warning: WShell not present under your ARexx, local environment variable USER can''t be checked'
-
- if USER == ''
- then do
- say 'warning: USER environment variable not set, mail will be sent to root'
- USER = 'root'
- end
-
- address command "filenote" scriptfile '"'USER'.'QUEUE'.'execdate'-'HOUR||MINUTE'"'
-
- call ScheduleJob ID User Queue execdate Hour||Minute
-
- exit
-
- /* ***************************************************************** */
- /* Functions */
- /* ***************************************************************** */
-
- ParseParameters: /* Parse the parameters specified to At */
-
- if left(option, 2) == '-q'
- then do
-
- if length(option) ~= 3
- then call ErrorExit 'at: invalid ''-q'' parameter'
-
- QUEUE = right(option, 1)
-
- if pos(QUEUE, xrange('a','z')) == 0
- then call ErrorExit 'at: invalid queue name,' QUEUE
-
- parse arg option line
- end
- else parse arg line
-
- IncrementPart = ""
- parse var line line '+' IncrementPart
- parse var line line xline
- parse var IncrementPart Increment Unit .
-
- UnitNo = 1 /* default to minutes */
-
- if Unit ~= ""
- then do
- AbbrevUnit = upper( left( Unit, 3 ) )
-
- UnitNo = find( "MIN HOU DAY WEE MON YEA", AbbrevUnit )
-
- if UnitNo = 0
- then call ErrorExit "at: Invalid increment unit:" Unit
- end
-
- if Increment ~= ""
- then do
- if ~ datatype( Increment, "N" )
- then call ErrorExit "at: Invalid increment:" Increment
- end
-
- if QUEUE == 'b'
- then do
-
- if line ~= ''
- then call ErrorExit 'at: time can''t be given for use with b[atch] queue'
-
- HOUR = '*'
- MINUTE = '*'
- end
- else do
-
- if line == ''
- then do
- say 'at: time must be given for use with' QUEUE 'queue'
- exit 10
- end
-
- nowdate = date( 's' )
- now = time()
- nowhour = left( now, 2 )
- nowminute = substr( now, 4, 2 )
-
- if upper( line ) ~= "NOW"
- then do
-
- if length(line) > 4 | ~datatype(line, 'N')
- then call ErrorExit 'at: "'line'" is not a valid time, must be of form: H, HH, HMM, HHMM'
-
- if length(line) > 2
- then do
- MINUTE = right(line, 2)
- HOUR = left(line, length(line) - 2)
- end
- else HOUR = line
-
- if MINUTE < 0 | MINUTE > 59 | HOUR < 0 | HOUR > 23
- then call ErrorExit 'at: "'line'" is not in the range of a valid time'
- end
- else do
- HOUR = nowhour
- MINUTE = nowminute
- end
-
- /*trace ?i*/
-
- execdate = nowdate
-
- if xline ~= ""
- then do
- parse var xline d1 d2 d3 .
-
- if d2 ~= "" & ~ datatype( d1, "N" ) & datatype( d2, "N" )
- then do
- temp = d1
- d1 = d2
- d2 = temp
- end
-
- if d3 ~= ""
- then do
- if ~ datatype( d3, "N" ) | length( d3 ) > 4
- then call ErrorExit "at: Invalid year -" d1
-
- if d3 < 100 & d3 >= substr( nowdate, 3, 2 )
- then execdate = "19"right( "00"d3, 2 ) || substr( execdate, 5 )
- else execdate = overlay( d3, "2000", 5 - length( d3 ) ) || substr( execdate, 5 )
- end
-
- if d2 ~= ""
- then do
- Month = find( "JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC", upper( d2 ) )
-
- if Month = 0
- then call ErrorExit "at: Invalid month -" d2
-
- execdate = left( execdate, 4 ) || right( "00"Month, 2 ) || right( execdate, 2 )
- end
-
- if d1 ~= ""
- then do
- if ~ datatype( d1, "N" )
- then call ErrorExit "at: Invalid day of month -" d1
-
- execdate = left( execdate, 6 ) || right( "00"d1, 2 )
- end
- end
-
- if Increment ~= ""
- then do
-
- /*trace ?i*/
- if UnitNo <= 4
- then do
- UDayno = date( 'i', execdate, 's' )
-
- if UnitNo = 1
- then Minute = Minute + Increment
- else if UnitNo = 2
- then Hour = Hour + Increment
- else if UnitNo = 3
- then UDayno = UDayno + Increment
- else if UnitNo = 4
- then UDayno = UDayno + Increment * 7
-
- Hour = right( "00" || Hour + Minute % 60, 2 )
- Minute = right( "00" || Minute // 60, 2 )
- UDayno = Udayno + Hour % 24
- execdate = date( 's', Udayno, 'i' )
- end
- else do
- Year = left( execdate, 4 )
-
- if UnitNo = 5
- then do
- Month = substr( execdate, 5, 2 ) + Increment
- Year = Year + ( Month - 1 ) % 12
- Month = right( "00" || ( Month - 1 ) // 12 + 1, 2 )
- execdate = Year || Month || substr( execdate, 7 )
- end
- else do
- execdate = Year + Increment || substr( execdate, 5 )
- end
- end
- end
-
- if execdate'@'Hour':'Minute <= nowdate'@'nowhour':'nowminute
- then do
- if xline = ""
- then execdate = date( 's', date('i') + 1 )
- else if d2 = ""
- then do
- if substr( execdate, 5, 2 ) = 12
- then execdate = left( execdate, 4 ) + 1 || "01" || right( execdate, 2 )
- else execdate = left( execdate, 4 ) || right( "00"substr( execdate, 5, 2 ) + 1, 2 ) || right( execdate, 2 )
- end
- else if d3 = ""
- then do
- execdate = left( execdate, 4 ) + 1 || substr( execdate, 5 )
- end
- else call ErrorExit "at: Too late for" date("n", execdate, "s") Hour":"Minute
- end
-
- Day = substr( execdate, 7, 2 )
- Month = substr( execdate, 5, 2 )
- Days = word( "31 28 31 30 31 30 31 31 30 31 30 31", Month )
-
- if Month = 2 & left( execdate, 4 ) // 4 = 0
- then Days = 29
-
- if day > Days & UnitNo = 5
- then do
- day = Days
- execdate = left( execdate, 6 ) || day
- end
-
- if day < 1 | day > Days
- then call ErrorExit "at: Invalid day of month -" d1
-
- /*say execdate Hour":"Minute*/
- /*exit 20*/
-
- end
-
- return
-
- /* ***************************************************************** */
-
- ErrorExit:
-
- parse arg message
-
- say message
-
- exit 10
-
- /* ***************************************************************** */
-
- GetCronEvents:
-
- /*trace ?i*/
-
- events = ""
-
- if show( "ports", CYBERCRON )
- then do
- address CYBERCRON 'LIST_EVENTS'
- eventlist = result
- numwords = words(eventlist)
-
- if (numwords > 0) & (word(eventlist,1) ~= "<None>")
- then do
-
- do j = 1 to numwords
- event = word( eventlist, j )
- address CYBERCRON 'SHOW_EVENT' event
- events = events || '0a'x || result
- end
- end
- end
-
- if events ~= ""
- then events = substr( events, 2 )
-
- return events
-
- /* ***************************************************************** */
-
- ListJob: procedure expose AT_FILES CRONLOG NJobs Events Header
-
- parse arg Job
-
- if ~ datatype( NJobs, "N" )
- then NJobs = 0
-
- JobInfo = statef( AT_FILES || Job )
-
- if JobInfo = ""
- then do
- say "Job" Job "not found"
- return
- end
-
- parse var JobInfo . . . . fdays fmins fticks User '.' queue '.' edate '-' etime
-
- ftime = right( "0000"etime, 4 )
- ftime = left( ftime, 2)":"right( ftime, 2 )
-
- User = strip( User )
-
- if index( Events, AT_FILES || Job ) ~= 0
- then do
-
- jsched = "Scheduled"
- if Queue = 'b'
- then do
- if bMax = "BMAX"
- then do
- address CYBERCRON GET_QUEUE_MAX b
- bMax = rc /* Cybercron passes answer in rc ! */
- end
-
- if bMax = 0
- then jsched = "Held"
- else jsched = "Waiting"
- end
-
- jstat = ""
- NJobs = NJobs + 1
- end
- else do
- JobStartInfo = SearchCronLog( Job )
-
- if JobStartInfo = ""
- then do
- jsched = "NOT SCHEDULED"
- jstat = "***"
- end
- else do
- parse var JobStartInfo '(' JobStartDate ')' . JobNo jstart JobFileName
- jsched = jstart JobStartdate
- end
- end
-
- if Header ~= ""
- then do
- say "User Job Q Date Time Status"
- Header = ""
- end
-
- if Queue ~= 'b'
- then jexec = date("n", edate, "s") ftime
- else jexec = " "
-
-
- say left( User, 16 ) Job Queue jexec jsched
-
- return
-
- /* ***************************************************************** */
-
- ListAt: procedure expose AT_FILES CRONLOG
-
- parse arg ID
-
- NJobs = 0
-
- Events = GetCronEvents()
-
- if ID ~= ""
- then do
- Job = right( "0000"ID, 4 )
- call ListJob( Job )
- if open( fh, AT_FILES || Job, "r" )
- then do
- say "-------------------------------------------------"
- do while ~eof( fh )
- Line = readln( fh )
- if Line = '; ******** End of Prologue *******'
- then break
- end
-
- do while ~eof( fh )
- Line = readln( fh )
- if Line = '; ****** Start of Epilogue *******'
- then break
-
- say Line
- end
-
- call close fh
- end
-
- exit
- end
-
- Jobs = showdir( AT_FILES )
-
- /*trace ?i*/
-
- do i = 1 to words( Jobs )
- Job = word( Jobs, i )
-
- if upper( Job ) ~= "AT.SEQ"
- then call ListJob Job
- end
-
- say NJobs "jobs waiting"
-
- return
-
- /* ***************************************************************** */
-
- ScheduleJob: procedure expose AT_FILES
-
- parse arg Job User Queue edate etime .
-
- if Queue ~= 'b'
- then do
- Day = right( edate, 2 )
- Month = substr( edate, 5 , 2 )
- Hour = left( etime, 2 )
- Minute = right( etime, 2 )
- end
- else do
- address CYBERCRON get_queue_max b
- qbmax = rc /* Cybercron passes answer in rc ! */
- if qbmax = 0
- then bStatus = "awaiting resources"
- else bStatus = "will be scheduled soon"
-
- address CYBERCRON 'ADD_EVENT :MAILUSER' USER ':EXECONCE :OBEYQUEUE' QUEUE '* * * * *' AT_FILES || Job
- if rc = 0
- then say "Job" Job bStatus
- else say "Job" Job "schedule failed"
-
- return
- end
-
- nowdate = date( 's' )
- now = time()
- nowtime = left( now, 2 ) || substr( now, 4, 2 )
-
- yearstime = left( nowdate, 4 ) + 1 || substr( nowdate, 5 )
-
- if edate'@'etime <= nowdate'@'nowtime
- then do
- say "at: Warning: job" Job "not scheduled - too late"
- end
- else if edate'@'etime < yearstime'@'now
- then do
- address CYBERCRON 'ADD_EVENT :MAILUSER' USER ':EXECONCE :OBEYQUEUE' QUEUE MINUTE HOUR DAY MONTH '*' AT_FILES || Job
- if rc = 0
- then say "Job" Job "scheduled for" date( "n", edate, "s" ) Hour":"Minute
- else say "Job" Job "schedule failed"
- end
- else do
- say "at: Warning: job" Job "not scheduled - too far in the future"
- end
-
- return
-
- /* ***************************************************************** */
-
- ReSchedule: procedure expose AT_FILES
-
- parse arg ID
-
- Jobs = showdir( AT_FILES )
-
- Events = GetCronEvents()
-
- do i = 1 to words( Jobs )
- Job = word( Jobs, i )
-
- if upper( Job ) = "AT.SEQ"
- then iterate
-
- if index( Events, AT_FILES || Job ) = 0
- then do
- JobInfo = statef( AT_FILES || Job )
-
- if JobInfo = ""
- then do
- say "Job" Job "not found"
- iterate
- end
-
- parse var JobInfo . . . . fdays fmins fticks User '.' queue '.' edate '-' etime
-
- call ScheduleJob Job User Queue edate etime
- end
- end
-
- return
-
- /* ***************************************************************** */
-
- RemoveAt: procedure expose AT_FILES
-
- parse arg Job
-
- Job = right( "0000"Job, 4 )
-
- if ~ exists( AT_FILES || Job )
- then call ErrorExit "at: job" Job "not found."
-
- Events = GetCronEvents()
-
- do while Events ~= ""
- parse var Events EventLine '0a'x Events
-
- if index( EventLine, AT_FILES || Job ) ~= 0
- then break
-
- EventLine = ""
- end
-
- if EventLine ~= ""
- then do
- parse var EventLine EventID .
- address cybercron delete_event EventID
-
- if rc ~= 0
- then say "at: warning - couldn't remove CyberCRON event"
- end
-
- call delete AT_FILES || Job
-
- say "Job" Job "removed."
-
- return
-
- /* ***************************************************************** */
-
- GenerateID: procedure expose AT_FILES
-
- if open( fh, AT_FILES || "at.seq", "r" )
- then do
- ID = readln( fh )
- call close fh
- end
- else ID = 0
-
- if ID > 9999
- then ID = 0
-
- ID = ID + 1
-
- if open( fh, AT_FILES || "at.seq", "w" )
- then do
- call writeln( fh, id )
- call close fh
- end
-
- return right( "0000"ID, 4 )
-
- /* ***************************************************************** */
-
- SearchCronLog: procedure expose AT_FILES CRONLOG
-
- parse arg ID
-
- /*trace ?i*/
-
- if ~ exists( CRONLOG )
- then return ""
-
- /*"execio" read CRONLOG locate AT_FILES || ID var Startinfo*/
-
- tfile = "T:at-info-" || ID
-
-
- "search >"tfile CRONLOG AT_FILES || ID
-
- if rc = 0
- then do
- if open( fht, tfile, "r" )
- then do
- Startinfo = readln( fht )
- call close( fht )
- end
- else Startinfo = ""
- end
- else Startinfo = ""
-
- call delete( tfile )
-
- return Startinfo
-