home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / amiga / utility / misc / cyber15.lha / CyberCron / RexxScripts / at.rexx next >
Encoding:
OS/2 REXX Batch file  |  1992-09-30  |  15.2 KB  |  721 lines

  1. /*rx
  2.  *  at.rexx --- simulate UNIX at command.  For use with CyberCron.
  3.  *  By Loren J. Rittle (rittle@comm.mot.com) - Tue May  5 01:47:11 1992
  4.  *  Updated to work with any AmigaOS shell   - Wed May  6 03:32:00 1992
  5.  *
  6.  *  Copyright ⌐ 1992  Loren J. Rittle
  7.  *  Use as you will, just document your changes and keep my copyright
  8.  *  notice intact.  Feel free to mail enhancements to me.
  9.  *
  10.  *  Modifications ⌐ 1992 by Graham Walter (gwalter@gwalter.demon.co.uk)
  11.  *  Following added:
  12.  *    Ability to specify date, incremental time periods
  13.  *    Remove (-r option) jobs
  14.  *    List (-l option)
  15.  *      Schedule (-s option) - used to reschedule jobs if
  16.  *        CyberCron is restarted (due to eg reboot)
  17.  *
  18.  *  Usage:
  19.  *    at [-q[ac-z]] time [day [month [year]] [+ increment timeperiod] 
  20.  *    <shell script>
  21.  *    <EOF>
  22.  *
  23.  *    at -qb
  24.  *    <shell script>
  25.  *    <EOF>
  26.  *
  27.  *    at -l [<task name as given at issue time, or from list>]
  28.  *      [for list current 'at' job(s)]
  29.  *
  30.  *    at -r <task name as given at issue time, or from list>
  31.  *      [remove an 'at' job as shown in list]
  32.  *
  33.  *    at -s 
  34.  *      [schedule any unscheduled jobs]
  35.  */
  36.  
  37. /* USER MODIFIABLE DEFAULT, EDIT TO TASTE */
  38.  
  39. /* AT_FILES should be somewhere that only at writes to.
  40.    T: is not a suitable location, IMHO.  In the future, at.rexx
  41.    and some startup code magic might know how to requeue jobs
  42.    that should have been run while power was off or after a crash
  43.    occurred.  To prepare for this, AT_FILES should be on a real
  44.    file system. */
  45. AT_FILES = 's:at/'
  46. CRONLOG  = "T:CronLog"
  47.  
  48. /* NO GENERAL USER MODIFIABLE PARTS BELOW THIS COMMENT. */
  49.  
  50. /* Default queue to place jobs in.  Remember queue 'b' is special. */
  51. QUEUE = 'a'
  52.  
  53. HOUR = '00'
  54. MINUTE = '00'
  55.  
  56. options results
  57.  
  58. parse arg Parameters
  59. parse arg option line xline
  60.  
  61. if left( option, 2 ) = "-l"
  62. then do
  63.     call ListAt line
  64.     exit
  65. end
  66.  
  67. if left( option, 2 ) = "-r"
  68. then do
  69.     call RemoveAt line
  70.     exit
  71. end
  72.  
  73. if left( option, 2 ) = "-s"
  74. then do
  75.     call ReSchedule line
  76.     exit
  77. end
  78.  
  79. call ParseParameters Parameters
  80.  
  81. do 100 until ~ exists( AT_FILES || ID )
  82.     ID = GenerateId()
  83. end
  84.  
  85. if exists( AT_FILES || ID )
  86. then call ErrorExit 'at: can''t find unused ID'
  87.  
  88. scriptfile = AT_FILES || ID
  89.  
  90. if ~open('sfh', scriptfile, 'W')
  91. then call ErrorExit 'at: can''t open' scriptfile 'for output'
  92.  
  93. call writeln('sfh', '; ****** Start of Prologue *******')
  94.  
  95. call writeln('sfh', '; ****** At Job' ID '      *******')
  96.  
  97. call writeln('sfh', 'cd' '22'x||pragma('D')||'22'x)
  98. call writeln('sfh', 'stack' pragma('S', 4000))
  99.  
  100. if left(address(), 4) == 'WSH_'
  101. then do
  102.     /*
  103.      *  If we have a WShell under us, then try to propagate
  104.      *  local environment variables.  If you don't, I'm not
  105.      *  sorry for you, read comments below. :-)
  106.      */
  107.     'set | execio stem VARS.'
  108.     do i = 1 to VARS.0
  109.           if find( "PROCESS RC RESULT2", upper( word(VARS.i, 1) ) ) = 0
  110.     then call writeln('sfh', 'set' VARS.i)
  111.     end
  112. end
  113. else say 'warning: WShell not present under your ARexx, local environment variables can''t be propagated'
  114.  
  115. call writeln('sfh', '; ******** End of Prologue *******')
  116.  
  117. line = readln( stdin )
  118.  
  119. do while ~ eof( stdin )
  120.     call writeln('sfh', line)
  121.     line = readln(stdin)
  122. end
  123.  
  124. call writeln('sfh', '; ****** Start of Epilogue *******')
  125. call writeln('sfh', 'run <nil: >nil: delete' scriptfile)
  126. call close('sfh')
  127.  
  128. address command 'protect' scriptfile '+s'
  129.  
  130. USER = getenv('USER')
  131. if left(address(), 4) == 'WSH_' then
  132.   do
  133.     /*
  134.      *  If we have a WShell under us, then try to get a local
  135.      *  environment variable with the name USER to override
  136.      *  the global USER setting.
  137.      *  If you don't have a WShell under you, then why not? :-)
  138.      *  Basically, you are less than human if you don't have a WShell
  139.      *  under you.  If everyone had a WShell under their ARexx,
  140.      *  I wouldn't have to write such weird ARexx code.  I hate you
  141.      *  if you don't WShell under your ARexx, cause you make my life
  142.      *  Hell!  Does anyone read these comments, or I'm I wasting
  143.      *  my time here? :-)
  144.      */
  145.     /* AmigaDOG braindamage: */
  146.     'get USER >nil:'
  147.     if rc == 0 then
  148.       'get USER | execio var USERX'
  149.     else
  150.       USERX = ''
  151.     if USERX ~= '' then
  152.       USER = USERX
  153.   end
  154. else
  155.   say 'warning: WShell not present under your ARexx, local environment variable USER can''t be checked'
  156.  
  157. if USER == ''
  158. then do
  159.     say 'warning: USER environment variable not set, mail will be sent to root'
  160.     USER = 'root'
  161. end
  162.  
  163. address command "filenote" scriptfile '"'USER'.'QUEUE'.'execdate'-'HOUR||MINUTE'"'
  164.  
  165. call ScheduleJob ID User Queue execdate Hour||Minute
  166.  
  167. exit
  168.  
  169. /* ***************************************************************** */ 
  170. /*                             Functions                             */ 
  171. /* ***************************************************************** */ 
  172.  
  173. ParseParameters:    /* Parse the parameters specified to At */
  174.  
  175. if left(option, 2) == '-q'
  176. then do
  177.  
  178.     if length(option) ~= 3
  179.     then call ErrorExit 'at: invalid ''-q'' parameter'
  180.  
  181.     QUEUE = right(option, 1)
  182.  
  183.     if pos(QUEUE, xrange('a','z')) == 0
  184.     then call ErrorExit 'at: invalid queue name,' QUEUE
  185.  
  186.     parse arg option line
  187. end
  188. else parse arg line
  189.  
  190. IncrementPart = ""
  191. parse var line line '+' IncrementPart
  192. parse var line line xline
  193. parse var IncrementPart Increment Unit .
  194.  
  195. UnitNo = 1    /* default to minutes */
  196.  
  197. if Unit ~= ""
  198. then do
  199.     AbbrevUnit = upper( left( Unit, 3 ) )
  200.  
  201.     UnitNo = find( "MIN HOU DAY WEE MON YEA", AbbrevUnit )
  202.  
  203.     if UnitNo = 0
  204.     then call ErrorExit "at: Invalid increment unit:" Unit
  205. end
  206.  
  207. if Increment ~= ""
  208. then do
  209.     if ~ datatype( Increment, "N" )
  210.     then call ErrorExit "at: Invalid increment:" Increment
  211. end
  212.  
  213. if QUEUE == 'b'
  214. then do
  215.  
  216.     if line ~= ''
  217.     then call ErrorExit 'at: time can''t be given for use with b[atch] queue'
  218.  
  219.     HOUR = '*'
  220.     MINUTE = '*'
  221. end
  222. else do
  223.  
  224.     if line == ''
  225.     then do
  226.     say 'at: time must be given for use with' QUEUE 'queue'
  227.     exit 10
  228.     end
  229.  
  230.     nowdate = date( 's' )
  231.     now = time()
  232.     nowhour = left( now, 2 )
  233.     nowminute = substr( now, 4, 2 )
  234.  
  235.     if upper( line ) ~= "NOW"
  236.     then do
  237.  
  238.         if length(line) > 4 | ~datatype(line, 'N')
  239.         then call ErrorExit 'at: "'line'" is not a valid time, must be of form: H, HH, HMM, HHMM'
  240.  
  241.         if length(line) > 2
  242.         then do
  243.         MINUTE = right(line, 2)
  244.         HOUR = left(line, length(line) - 2)
  245.         end
  246.         else HOUR = line
  247.  
  248.         if MINUTE < 0 | MINUTE > 59 | HOUR < 0 | HOUR > 23
  249.         then call ErrorExit 'at: "'line'" is not in the range of a valid time'
  250.     end
  251.     else do
  252.         HOUR = nowhour
  253.         MINUTE = nowminute
  254.     end
  255.  
  256. /*trace ?i*/
  257.  
  258.     execdate = nowdate
  259.  
  260.     if xline ~= ""
  261.     then do
  262.     parse var xline d1 d2 d3 .
  263.  
  264.     if d2 ~= "" & ~ datatype( d1, "N" ) & datatype( d2, "N" )
  265.     then do
  266.         temp = d1
  267.         d1 = d2
  268.         d2 = temp
  269.     end
  270.  
  271.     if d3 ~= ""
  272.     then do
  273.         if ~ datatype( d3, "N" ) | length( d3 ) > 4
  274.         then call ErrorExit "at: Invalid year -" d1
  275.  
  276.         if d3 < 100 & d3 >= substr( nowdate, 3, 2 )
  277.         then execdate = "19"right( "00"d3, 2 ) || substr( execdate, 5 )
  278.         else execdate = overlay( d3, "2000", 5 - length( d3 ) ) || substr( execdate, 5 )
  279.     end
  280.  
  281.     if d2 ~= ""
  282.     then do
  283.         Month = find( "JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC", upper( d2 ) )
  284.  
  285.         if Month = 0
  286.         then call ErrorExit "at: Invalid month -" d2
  287.  
  288.         execdate = left( execdate, 4 ) || right( "00"Month, 2 ) || right( execdate, 2 )
  289.     end
  290.  
  291.     if d1 ~= ""
  292.     then do
  293.         if ~ datatype( d1, "N" )
  294.         then call ErrorExit "at: Invalid day of month -" d1
  295.  
  296.         execdate = left( execdate, 6 ) || right( "00"d1, 2 )
  297.     end
  298.     end
  299.  
  300.     if Increment ~= ""
  301.     then do
  302.  
  303. /*trace ?i*/
  304.         if UnitNo <= 4
  305.         then do
  306.             UDayno = date( 'i', execdate, 's' )
  307.  
  308.             if UnitNo = 1
  309.             then Minute = Minute + Increment
  310.             else if UnitNo = 2
  311.             then Hour = Hour + Increment
  312.             else if UnitNo = 3
  313.             then UDayno = UDayno + Increment
  314.             else if UnitNo = 4
  315.             then UDayno = UDayno + Increment * 7
  316.  
  317.                 Hour = right( "00" || Hour + Minute % 60, 2 )
  318.                 Minute = right( "00" || Minute // 60, 2 )
  319.             UDayno = Udayno + Hour % 24
  320.             execdate = date( 's', Udayno, 'i' )
  321.         end
  322.         else do
  323.             Year = left( execdate, 4 )
  324.  
  325.             if UnitNo = 5
  326.             then do
  327.                 Month = substr( execdate, 5, 2 ) + Increment
  328.                 Year = Year + ( Month - 1 ) % 12
  329.                 Month = right( "00" || ( Month - 1 ) // 12 + 1, 2 )
  330.                 execdate = Year || Month || substr( execdate, 7 )
  331.             end
  332.             else do
  333.                 execdate = Year + Increment || substr( execdate, 5 )
  334.             end
  335.         end
  336.     end
  337.  
  338.     if execdate'@'Hour':'Minute <= nowdate'@'nowhour':'nowminute
  339.     then do
  340.     if xline = ""
  341.     then execdate = date( 's', date('i') + 1 )
  342.     else if d2 = ""
  343.     then do
  344.         if substr( execdate, 5, 2 ) = 12
  345.         then execdate = left( execdate, 4 ) + 1 || "01" || right( execdate, 2 )
  346.         else execdate = left( execdate, 4 ) || right( "00"substr( execdate, 5, 2 ) + 1, 2 ) || right( execdate, 2 )
  347.     end
  348.     else if d3 = ""
  349.     then do
  350.         execdate = left( execdate, 4 ) + 1 || substr( execdate, 5 )
  351.     end
  352.     else call ErrorExit "at: Too late for" date("n", execdate, "s") Hour":"Minute
  353.     end
  354.  
  355.     Day = substr( execdate, 7, 2 )
  356.     Month = substr( execdate, 5, 2 )
  357.     Days = word( "31 28 31 30 31 30 31 31 30 31 30 31", Month )
  358.  
  359.     if Month = 2 & left( execdate, 4 ) // 4 = 0
  360.     then Days = 29
  361.  
  362.     if day > Days & UnitNo = 5
  363.     then do
  364.         day = Days
  365.         execdate = left( execdate, 6 ) || day
  366.     end
  367.  
  368.     if day < 1 | day > Days
  369.     then call ErrorExit "at: Invalid day of month -" d1
  370.  
  371. /*say execdate Hour":"Minute*/
  372. /*exit 20*/
  373.  
  374. end
  375.  
  376. return
  377.  
  378. /* ***************************************************************** */ 
  379.  
  380. ErrorExit:
  381.  
  382. parse arg message
  383.  
  384. say message
  385.  
  386. exit 10
  387.  
  388. /* ***************************************************************** */ 
  389.  
  390. GetCronEvents:
  391.  
  392. /*trace ?i*/
  393.  
  394. events = ""
  395.  
  396. if show( "ports", CYBERCRON )
  397. then do
  398.     address CYBERCRON 'LIST_EVENTS'
  399.     eventlist = result
  400.     numwords = words(eventlist)
  401.  
  402.     if (numwords > 0) & (word(eventlist,1) ~= "<None>")
  403.     then do
  404.  
  405.         do j = 1 to numwords
  406.             event = word( eventlist, j )
  407.             address CYBERCRON 'SHOW_EVENT' event
  408.             events = events || '0a'x || result
  409.         end
  410.     end
  411. end
  412.  
  413. if events ~= ""
  414. then events = substr( events, 2 )
  415.  
  416. return events
  417.  
  418. /* ***************************************************************** */ 
  419.  
  420. ListJob: procedure expose AT_FILES CRONLOG NJobs Events Header
  421.  
  422. parse arg Job
  423.  
  424. if ~ datatype( NJobs, "N" )
  425. then NJobs = 0
  426.  
  427. JobInfo = statef( AT_FILES || Job )
  428.  
  429. if JobInfo = ""
  430. then do
  431.     say "Job" Job "not found"
  432.     return
  433. end
  434.  
  435. parse var JobInfo . . . . fdays fmins fticks User '.' queue '.' edate '-' etime
  436.  
  437. ftime = right( "0000"etime, 4 )
  438. ftime = left( ftime, 2)":"right( ftime, 2 )
  439.  
  440. User = strip( User )
  441.  
  442. if index( Events, AT_FILES || Job ) ~= 0
  443. then do
  444.  
  445.     jsched = "Scheduled"
  446.     if Queue = 'b'
  447.     then do
  448.         if bMax = "BMAX"
  449.         then do
  450.             address CYBERCRON GET_QUEUE_MAX b
  451.             bMax = rc    /* Cybercron passes answer in rc ! */
  452.         end
  453.  
  454.         if bMax = 0
  455.         then jsched = "Held"
  456.         else jsched = "Waiting"
  457.     end
  458.  
  459.     jstat = ""
  460.     NJobs = NJobs + 1
  461. end
  462. else do
  463.     JobStartInfo = SearchCronLog( Job )
  464.     
  465.     if JobStartInfo = ""
  466.     then do
  467.         jsched = "NOT SCHEDULED"
  468.         jstat = "***"
  469.     end
  470.     else do
  471.         parse var JobStartInfo '(' JobStartDate ')' . JobNo jstart JobFileName
  472.         jsched = jstart JobStartdate
  473.     end
  474. end
  475.  
  476. if Header ~= ""
  477. then do
  478.     say "User             Job  Q Date        Time  Status"
  479.     Header = ""
  480. end
  481.  
  482. if Queue ~= 'b'
  483. then jexec = date("n", edate, "s") ftime
  484. else jexec = "                 "
  485.  
  486.  
  487. say left( User, 16 ) Job Queue jexec jsched
  488.  
  489. return
  490.  
  491. /* ***************************************************************** */ 
  492.  
  493. ListAt: procedure expose AT_FILES CRONLOG
  494.  
  495. parse arg ID
  496.  
  497. NJobs = 0
  498.  
  499. Events = GetCronEvents()
  500.  
  501. if ID ~= ""
  502. then do
  503.     Job = right( "0000"ID, 4 ) 
  504.     call ListJob( Job )
  505.     if open( fh, AT_FILES || Job, "r" )
  506.     then do
  507.         say "-------------------------------------------------"
  508.         do while ~eof( fh )
  509.             Line = readln( fh )
  510.             if Line = '; ******** End of Prologue *******'
  511.             then break
  512.         end
  513.  
  514.         do while ~eof( fh )
  515.             Line = readln( fh )
  516.             if Line = '; ****** Start of Epilogue *******'
  517.             then break
  518.  
  519.             say Line
  520.         end
  521.  
  522.         call close fh
  523.     end
  524.  
  525.     exit
  526. end
  527.  
  528. Jobs = showdir( AT_FILES )
  529.  
  530. /*trace ?i*/
  531.  
  532. do i = 1 to words( Jobs )
  533.     Job = word( Jobs, i )
  534.  
  535.     if upper( Job ) ~= "AT.SEQ"
  536.     then call ListJob Job
  537. end
  538.  
  539. say NJobs "jobs waiting"
  540.  
  541. return
  542.  
  543. /* ***************************************************************** */ 
  544.  
  545. ScheduleJob: procedure expose AT_FILES
  546.  
  547. parse arg Job User Queue edate etime .
  548.  
  549. if Queue ~= 'b'
  550. then do
  551.     Day    = right( edate, 2 )
  552.     Month  = substr( edate, 5 , 2 )
  553.     Hour   = left( etime, 2 )
  554.     Minute = right( etime, 2 )
  555. end
  556. else do
  557.     address CYBERCRON get_queue_max b
  558.     qbmax = rc            /* Cybercron passes answer in rc ! */
  559.     if qbmax = 0
  560.     then bStatus = "awaiting resources"
  561.     else bStatus = "will be scheduled soon"
  562.  
  563.     address CYBERCRON 'ADD_EVENT :MAILUSER' USER ':EXECONCE :OBEYQUEUE' QUEUE '* * * * *' AT_FILES || Job
  564.     if rc = 0
  565.     then say "Job" Job bStatus
  566.     else say "Job" Job "schedule failed"
  567.  
  568.     return
  569. end
  570.  
  571. nowdate = date( 's' )
  572. now = time()
  573. nowtime = left( now, 2 ) || substr( now, 4, 2 )
  574.  
  575. yearstime = left( nowdate, 4 ) + 1 || substr( nowdate, 5 )
  576.  
  577. if edate'@'etime <= nowdate'@'nowtime
  578. then do
  579.     say "at: Warning: job" Job "not scheduled - too late"
  580. end
  581. else if edate'@'etime < yearstime'@'now
  582. then do
  583.     address CYBERCRON 'ADD_EVENT :MAILUSER' USER ':EXECONCE :OBEYQUEUE' QUEUE MINUTE HOUR DAY MONTH '*' AT_FILES || Job
  584.     if rc = 0
  585.     then say "Job" Job "scheduled for" date( "n", edate, "s" ) Hour":"Minute
  586.     else say "Job" Job "schedule failed"
  587. end
  588. else do
  589.     say "at: Warning: job" Job "not scheduled - too far in the future"
  590. end
  591.  
  592. return
  593.  
  594. /* ***************************************************************** */ 
  595.  
  596. ReSchedule: procedure expose AT_FILES
  597.  
  598. parse arg ID
  599.  
  600. Jobs = showdir( AT_FILES )
  601.  
  602. Events = GetCronEvents()
  603.  
  604. do i = 1 to words( Jobs )
  605.     Job = word( Jobs, i )
  606.  
  607.     if upper( Job ) = "AT.SEQ"
  608.     then iterate
  609.  
  610.     if index( Events, AT_FILES || Job ) = 0
  611.     then do
  612.         JobInfo = statef( AT_FILES || Job )
  613.  
  614.         if JobInfo = ""
  615.         then do
  616.             say "Job" Job "not found"
  617.             iterate
  618.         end
  619.  
  620.         parse var JobInfo . . . . fdays fmins fticks User '.' queue '.' edate '-' etime
  621.  
  622.         call ScheduleJob Job User Queue edate etime
  623.     end
  624. end
  625.  
  626. return
  627.  
  628. /* ***************************************************************** */ 
  629.  
  630. RemoveAt: procedure expose AT_FILES
  631.  
  632. parse arg Job
  633.  
  634. Job = right( "0000"Job, 4 )
  635.  
  636. if ~ exists( AT_FILES || Job )
  637. then call ErrorExit "at: job" Job "not found."
  638.  
  639. Events = GetCronEvents()
  640.  
  641. do while Events ~= ""
  642.     parse var Events EventLine '0a'x Events
  643.  
  644.     if index( EventLine, AT_FILES || Job ) ~= 0
  645.     then break
  646.  
  647.     EventLine = ""
  648. end
  649.  
  650. if EventLine ~= ""
  651. then do
  652.     parse var EventLine EventID .
  653.     address cybercron delete_event EventID
  654.  
  655.     if rc ~= 0
  656.     then say "at: warning - couldn't remove CyberCRON event"
  657. end
  658.  
  659. call delete AT_FILES || Job
  660.  
  661. say "Job" Job "removed."
  662.  
  663. return
  664.  
  665. /* ***************************************************************** */ 
  666.  
  667. GenerateID: procedure expose AT_FILES
  668.  
  669. if open( fh, AT_FILES || "at.seq", "r" )
  670. then do
  671.     ID = readln( fh )
  672.     call close fh
  673. end
  674. else ID = 0
  675.  
  676. if ID > 9999
  677. then ID = 0
  678.  
  679. ID = ID + 1
  680.  
  681. if open( fh, AT_FILES || "at.seq", "w" )
  682. then do
  683.     call writeln( fh, id )
  684.     call close fh
  685. end
  686.  
  687. return right( "0000"ID, 4 )
  688.  
  689. /* ***************************************************************** */ 
  690.  
  691. SearchCronLog: procedure expose AT_FILES CRONLOG
  692.  
  693. parse arg ID
  694.  
  695. /*trace ?i*/
  696.  
  697. if ~ exists( CRONLOG )
  698. then return ""
  699.  
  700. /*"execio" read CRONLOG locate AT_FILES || ID var Startinfo*/
  701.  
  702. tfile = "T:at-info-" || ID
  703.  
  704.  
  705. "search >"tfile CRONLOG AT_FILES || ID
  706.  
  707. if rc = 0
  708. then do
  709.     if open( fht, tfile, "r" )
  710.     then do
  711.         Startinfo = readln( fht )
  712.         call close( fht )
  713.     end
  714.     else Startinfo = ""
  715. end
  716. else Startinfo = ""
  717.  
  718. call delete( tfile )
  719.  
  720. return Startinfo
  721.